From d4f95df2d2cf0cbc71485214adbccd251cf99258 Mon Sep 17 00:00:00 2001 From: rocketchat-github-ci Date: Wed, 21 Jun 2023 02:57:39 +0000 Subject: [PATCH 001/149] 6.2.7 --- .changeset/proud-cars-guess.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/proud-cars-guess.md diff --git a/.changeset/proud-cars-guess.md b/.changeset/proud-cars-guess.md new file mode 100644 index 000000000000..a845151cc840 --- /dev/null +++ b/.changeset/proud-cars-guess.md @@ -0,0 +1,2 @@ +--- +--- From a19359b3f5b09270fe1644df0706c6b1ebb979ef Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 16 Jun 2023 13:01:13 -0300 Subject: [PATCH 002/149] ci: Fixes to publish automation (#29563) --- .changeset/popular-llamas-notice.md | 5 +++ .changeset/wet-cherries-hunt.md | 5 +++ .github/workflows/new-release.yml | 7 ++-- .github/workflows/publish-release.yml | 4 +- .../release-action/src/bumpNextVersion.ts | 16 ++++---- .../src/fixWorkspaceVersionsBeforePublish.ts | 10 ++--- packages/release-action/src/gitUtils.ts | 6 --- packages/release-action/src/index.ts | 4 -- packages/release-action/src/publishRelease.ts | 20 +++++----- .../release-action/src/startPatchRelease.ts | 14 +++---- packages/release-action/src/utils.ts | 38 ++++++++++++++++--- 11 files changed, 78 insertions(+), 51 deletions(-) create mode 100644 .changeset/popular-llamas-notice.md create mode 100644 .changeset/wet-cherries-hunt.md delete mode 100644 packages/release-action/src/gitUtils.ts diff --git a/.changeset/popular-llamas-notice.md b/.changeset/popular-llamas-notice.md new file mode 100644 index 000000000000..dfcc5344e01d --- /dev/null +++ b/.changeset/popular-llamas-notice.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/release-action': patch +--- + +Use CI user PAT token diff --git a/.changeset/wet-cherries-hunt.md b/.changeset/wet-cherries-hunt.md new file mode 100644 index 000000000000..f16cd3df20d0 --- /dev/null +++ b/.changeset/wet-cherries-hunt.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/release-action': patch +--- + +Update versions in configured files diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml index f5869aa323c7..de6d66473201 100644 --- a/.github/workflows/new-release.yml +++ b/.github/workflows/new-release.yml @@ -28,6 +28,7 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 + token: ${{ secrets.CI_PAT }} - name: Setup NodeJS uses: ./.github/actions/setup-node @@ -48,7 +49,7 @@ jobs: action: bump env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.CI_PAT }} - name: Start patch release if: ${{ github.event.inputs.name == 'patch' }} @@ -58,7 +59,7 @@ jobs: base-ref: ${{ github.event.inputs.base-ref }} env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.CI_PAT }} - name: Publish release if: ${{ github.event.inputs.name == 'publish' }} @@ -68,4 +69,4 @@ jobs: base-ref: ${{ github.event.inputs.base-ref }} env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.CI_PAT }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index bb56beb975b9..74112cb86fd8 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -17,6 +17,8 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v3 + with: + token: ${{ secrets.CI_PAT }} - name: Setup NodeJS uses: ./.github/actions/setup-node @@ -36,4 +38,4 @@ jobs: action: publish-final env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.CI_PAT }} diff --git a/packages/release-action/src/bumpNextVersion.ts b/packages/release-action/src/bumpNextVersion.ts index 00a459e917a4..cc977900b213 100644 --- a/packages/release-action/src/bumpNextVersion.ts +++ b/packages/release-action/src/bumpNextVersion.ts @@ -7,7 +7,7 @@ import * as github from '@actions/github'; import { setupOctokit } from './setupOctokit'; import { createNpmFile } from './createNpmFile'; -import { getChangelogEntry, updateVersionPackageJson } from './utils'; +import { getChangelogEntry, bumpFileVersions, readPackageJson } from './utils'; import { fixWorkspaceVersionsBeforePublish } from './fixWorkspaceVersionsBeforePublish'; export async function bumpNextVersion({ @@ -27,6 +27,8 @@ export async function bumpNextVersion({ // TODO need to check if there is any change to 'main package', if not, there is no need to enter rc // and instead a normal release of the other packages should be done + const { version: currentVersion } = await readPackageJson(cwd); + // start release candidate await exec('yarn', ['changeset', 'pre', 'enter', 'rc']); @@ -34,9 +36,7 @@ export async function bumpNextVersion({ await exec('yarn', ['changeset', 'version']); // get version from main package - const mainPackageJsonPath = path.join(mainPackagePath, 'package.json'); - // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires - const { version: newVersion } = require(mainPackageJsonPath); + const { version: newVersion } = await readPackageJson(mainPackagePath); const mainPackageChangelog = path.join(mainPackagePath, 'CHANGELOG.md'); @@ -55,8 +55,8 @@ export async function bumpNextVersion({ const newBranch = `release-${finalVersion}`; // update root package.json - core.info('bump main package.json version'); - updateVersionPackageJson(cwd, newVersion); + core.info('update version in all files to new'); + await bumpFileVersions(cwd, currentVersion, newVersion); // TODO check if branch exists await exec('git', ['checkout', '-b', newBranch]); @@ -67,7 +67,9 @@ export async function bumpNextVersion({ core.info('fix dependencies in workspace packages'); await fixWorkspaceVersionsBeforePublish(); - await exec('yarn', ['changeset', 'publish']); + await exec('yarn', ['changeset', 'publish', '--no-git-tag']); + + await exec('git', ['tag', newVersion]); await exec('git', ['push', '--force', '--follow-tags', 'origin', `HEAD:refs/heads/${newBranch}`]); diff --git a/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts b/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts index cf60265724e8..1a5780996fef 100644 --- a/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts +++ b/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts @@ -13,6 +13,8 @@ import path from 'node:path'; import { getExecOutput } from '@actions/exec'; +import { readPackageJson } from './utils'; + const DEPENDENCY_TYPES = ['dependencies', 'devDependencies', 'peerDependencies']; export async function fixWorkspaceVersionsBeforePublish() { @@ -26,15 +28,13 @@ export async function fixWorkspaceVersionsBeforePublish() { // Get the version of each workspace package. const workspaceVersions = new Map(); for await (const workspace of workspaces) { - const packageJsonPath = path.join(workspace.location, 'package.json'); - const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')); + const packageJson = await readPackageJson(workspace.location); workspaceVersions.set(workspace.name, packageJson.version); } // Replace any `workspace:^` version ranges with the actual version. for await (const workspace of workspaces) { - const packageJsonPath = path.join(workspace.location, 'package.json'); - const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')); + const packageJson = await readPackageJson(workspace.location); for (const dependencyType of DEPENDENCY_TYPES) { const dependencies = Object.keys(packageJson[dependencyType] ?? {}); @@ -55,6 +55,6 @@ export async function fixWorkspaceVersionsBeforePublish() { } } - await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`); + await fs.writeFile(path.join(workspace.location, 'package.json'), `${JSON.stringify(packageJson, null, 2)}\n`); } } diff --git a/packages/release-action/src/gitUtils.ts b/packages/release-action/src/gitUtils.ts deleted file mode 100644 index 24afc9e49848..000000000000 --- a/packages/release-action/src/gitUtils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { exec } from '@actions/exec'; - -export async function setupGitUser() { - await exec('git', ['config', 'user.name', '"github-actions[bot]"']); - await exec('git', ['config', 'user.email', '"github-actions[bot]@users.noreply.github.com"']); -} diff --git a/packages/release-action/src/index.ts b/packages/release-action/src/index.ts index ec9fafa165ea..30beae4697f0 100644 --- a/packages/release-action/src/index.ts +++ b/packages/release-action/src/index.ts @@ -4,7 +4,6 @@ import path from 'path'; import * as core from '@actions/core'; import { publishRelease } from './publishRelease'; -import { setupGitUser } from './gitUtils'; import { bumpNextVersion } from './bumpNextVersion'; import { startPatchRelease } from './startPatchRelease'; @@ -23,9 +22,6 @@ import { startPatchRelease } from './startPatchRelease'; // process.chdir(inputCwd); // } - core.info('setting git user'); - await setupGitUser(); - core.info('setting GitHub credentials'); fs.writeFileSync(`${process.env.HOME}/.netrc`, `machine github.com\nlogin github-actions[bot]\npassword ${githubToken}`); diff --git a/packages/release-action/src/publishRelease.ts b/packages/release-action/src/publishRelease.ts index 4c6d1618ee38..a776c9d74d26 100644 --- a/packages/release-action/src/publishRelease.ts +++ b/packages/release-action/src/publishRelease.ts @@ -7,7 +7,7 @@ import * as core from '@actions/core'; import { createNpmFile } from './createNpmFile'; import { setupOctokit } from './setupOctokit'; -import { getChangelogEntry, updateVersionPackageJson } from './utils'; +import { bumpFileVersions, getChangelogEntry, readPackageJson } from './utils'; import { fixWorkspaceVersionsBeforePublish } from './fixWorkspaceVersionsBeforePublish'; export async function publishRelease({ @@ -32,6 +32,8 @@ export async function publishRelease({ await exec('git', ['checkout', baseRef]); } + const { version: currentVersion } = await readPackageJson(cwd); + if (exitCandidate) { let preRelease = false; try { @@ -54,10 +56,7 @@ export async function publishRelease({ // TODO if main package has no changes, throw error // get version from main package - const mainPackageJsonPath = path.join(mainPackagePath, 'package.json'); - - // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires - const { version: newVersion } = require(mainPackageJsonPath); + const { version: newVersion } = await readPackageJson(mainPackagePath); const mainPackageChangelog = path.join(mainPackagePath, 'CHANGELOG.md'); @@ -71,17 +70,18 @@ export async function publishRelease({ const releaseBody = changelogEntry.content; - // update root package.json - core.info('bump main package.json version'); - updateVersionPackageJson(cwd, newVersion); + core.info('update version in all files to new'); + await bumpFileVersions(cwd, currentVersion, newVersion); await exec('git', ['add', '.']); - await exec('git', ['commit', '-m', newVersion]); + await exec('git', ['commit', '-m', `Release ${newVersion}`]); core.info('fix dependencies in workspace packages'); await fixWorkspaceVersionsBeforePublish(); - await exec('yarn', ['changeset', 'publish']); + await exec('yarn', ['changeset', 'publish', '--no-git-tag']); + + await exec('git', ['tag', newVersion]); await exec('git', ['push', '--follow-tags']); diff --git a/packages/release-action/src/startPatchRelease.ts b/packages/release-action/src/startPatchRelease.ts index 320ee5e4c317..05681604e47c 100644 --- a/packages/release-action/src/startPatchRelease.ts +++ b/packages/release-action/src/startPatchRelease.ts @@ -1,18 +1,15 @@ -import path from 'path'; - import semver from 'semver'; import { exec } from '@actions/exec'; import * as github from '@actions/github'; import * as core from '@actions/core'; import { setupOctokit } from './setupOctokit'; -import { updateVersionPackageJson } from './utils'; +import { readPackageJson } from './utils'; export async function startPatchRelease({ githubToken, baseRef, mainPackagePath, - cwd = process.cwd(), }: { baseRef: string; mainPackagePath: string; @@ -24,9 +21,7 @@ export async function startPatchRelease({ await exec('git', ['checkout', baseRef]); // get version from main package - const mainPackageJsonPath = path.join(mainPackagePath, 'package.json'); - // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires - const { version } = require(mainPackageJsonPath); + const { version } = await readPackageJson(mainPackagePath); const newVersion = semver.inc(version, 'patch'); if (!newVersion) { @@ -38,8 +33,9 @@ export async function startPatchRelease({ // TODO check if branch exists await exec('git', ['checkout', '-b', newBranch]); - core.info('bump main package.json version'); - updateVersionPackageJson(cwd, newVersion); + // create empty changeset to have something to commit. the changeset file will be removed later in the process + core.info('create empty changeset'); + await exec('yarn', ['changeset', 'add', '--empty']); await exec('git', ['add', '.']); await exec('git', ['commit', '-m', newVersion]); diff --git a/packages/release-action/src/utils.ts b/packages/release-action/src/utils.ts index b8d8de9df66c..01819d936eb8 100644 --- a/packages/release-action/src/utils.ts +++ b/packages/release-action/src/utils.ts @@ -1,5 +1,5 @@ -import fs from 'fs'; import path from 'path'; +import { readFile, writeFile } from 'fs/promises'; import unified from 'unified'; import remarkParse from 'remark-parse'; @@ -58,9 +58,35 @@ export function getChangelogEntry(changelog: string, version: string) { }; } -export function updateVersionPackageJson(cwd = process.cwd(), newVersion: string) { - const rootPackageJsonPath = path.resolve(cwd, 'package.json'); - const content = fs.readFileSync(rootPackageJsonPath, 'utf8'); - const updatedContent = content.replace(/"version": ".*",$/m, `"version": "${newVersion}",`); - fs.writeFileSync(rootPackageJsonPath, updatedContent); +export async function readPackageJson(cwd: string) { + const filePath = path.resolve(cwd, 'package.json'); + return JSON.parse(await readFile(filePath, 'utf-8')); +} + +async function getUpdateFilesList(cwd: string): Promise { + const file = await readPackageJson(cwd); + if (!file.houston) { + return []; + } + const { houston } = file; + + if (!houston.updateFiles) { + return []; + } + + return houston.updateFiles; +} + +export async function bumpFileVersions(cwd: string, oldVersion: string, newVersion: string) { + const files = await getUpdateFilesList(cwd); + + await Promise.all( + files.map(async (file) => { + const filePath = path.join(cwd, file); + + const data = await readFile(filePath, 'utf8'); + + await writeFile(filePath, data.replace(oldVersion, newVersion), 'utf8'); + }), + ); } From 296f4bc976418c005b3acabc221ff12f6d43b151 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 20 Jun 2023 23:49:25 -0300 Subject: [PATCH 003/149] ci(release): setup git user --- packages/release-action/src/gitUtils.ts | 6 ++++++ packages/release-action/src/index.ts | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 packages/release-action/src/gitUtils.ts diff --git a/packages/release-action/src/gitUtils.ts b/packages/release-action/src/gitUtils.ts new file mode 100644 index 000000000000..0550841f2810 --- /dev/null +++ b/packages/release-action/src/gitUtils.ts @@ -0,0 +1,6 @@ +import { exec } from '@actions/exec'; + +export async function setupGitUser() { + await exec('git', ['config', 'user.name', '"rocketchat-github-ci"']); + await exec('git', ['config', 'user.email', '"buildmaster@rocket.chat"']); +} diff --git a/packages/release-action/src/index.ts b/packages/release-action/src/index.ts index 30beae4697f0..1e2e446cfa66 100644 --- a/packages/release-action/src/index.ts +++ b/packages/release-action/src/index.ts @@ -6,6 +6,7 @@ import * as core from '@actions/core'; import { publishRelease } from './publishRelease'; import { bumpNextVersion } from './bumpNextVersion'; import { startPatchRelease } from './startPatchRelease'; +import { setupGitUser } from './gitUtils'; // const getOptionalInput = (name: string) => core.getInput(name) || undefined; @@ -22,6 +23,9 @@ import { startPatchRelease } from './startPatchRelease'; // process.chdir(inputCwd); // } + core.info('setting git user'); + await setupGitUser(); + core.info('setting GitHub credentials'); fs.writeFileSync(`${process.env.HOME}/.netrc`, `machine github.com\nlogin github-actions[bot]\npassword ${githubToken}`); From 74983f5b46abacbde2afb0838681ef3c5023307d Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Tue, 20 Jun 2023 16:38:15 -0300 Subject: [PATCH 004/149] fix: Wrong IP usage on monolith TCP transporter configuration (#29551) Co-authored-by: Diego Sampaio --- .changeset/five-clouds-obey.md | 5 +++ .../local-services/instance/getLogger.ts | 35 +++++++++++++++++++ .../local-services/instance/getTransporter.ts | 3 +- .../server/local-services/instance/service.ts | 15 ++++++-- .../modules/watchers/watchers.module.ts | 5 +++ 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 .changeset/five-clouds-obey.md create mode 100644 apps/meteor/ee/server/local-services/instance/getLogger.ts diff --git a/.changeset/five-clouds-obey.md b/.changeset/five-clouds-obey.md new file mode 100644 index 000000000000..2a00dc5ce031 --- /dev/null +++ b/.changeset/five-clouds-obey.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Wrong IP usage on monolith TCP transporter configuration diff --git a/apps/meteor/ee/server/local-services/instance/getLogger.ts b/apps/meteor/ee/server/local-services/instance/getLogger.ts new file mode 100644 index 000000000000..bbab1ad28a6f --- /dev/null +++ b/apps/meteor/ee/server/local-services/instance/getLogger.ts @@ -0,0 +1,35 @@ +import { pino } from 'pino'; + +export function getLogger({ MOLECULER_LOG_LEVEL: level, NODE_ENV: mode }: Record = {}) { + if (!level || typeof level !== 'string') { + return {}; + } + + if (!['fatal', 'error', 'warn', 'info', 'debug', 'trace'].includes(level)) { + return {}; + } + + return { + logger: { + type: 'Pino', + options: { + level, + pino: { + options: { + timestamp: pino.stdTimeFunctions.isoTime, + ...(mode !== 'production' + ? { + transport: { + target: 'pino-pretty', + options: { + colorize: true, + }, + }, + } + : {}), + }, + }, + }, + }, + }; +} diff --git a/apps/meteor/ee/server/local-services/instance/getTransporter.ts b/apps/meteor/ee/server/local-services/instance/getTransporter.ts index c075dd2ad357..747321ea0edb 100644 --- a/apps/meteor/ee/server/local-services/instance/getTransporter.ts +++ b/apps/meteor/ee/server/local-services/instance/getTransporter.ts @@ -1,4 +1,4 @@ -export function getTransporter({ transporter, port }: { transporter?: string; port?: string } = {}) { +export function getTransporter({ transporter, port, extra }: { transporter?: string; port?: string; extra?: string } = {}) { if (transporter) { if (!transporter.match(/^(?:monolith\+)/)) { throw new Error('invalid transporter'); @@ -11,5 +11,6 @@ export function getTransporter({ transporter, port }: { transporter?: string; po return { port: port ? port.trim() : 0, udpDiscovery: false, + ...(extra ? JSON.parse(extra) : {}), }; } diff --git a/apps/meteor/ee/server/local-services/instance/service.ts b/apps/meteor/ee/server/local-services/instance/service.ts index c48481bc6428..f26db7ed78e6 100644 --- a/apps/meteor/ee/server/local-services/instance/service.ts +++ b/apps/meteor/ee/server/local-services/instance/service.ts @@ -9,6 +9,9 @@ import { InstanceStatus } from '@rocket.chat/instance-status'; import { StreamerCentral } from '../../../../server/modules/streamer/streamer.module'; import type { IInstanceService } from '../../sdk/types/IInstanceService'; import { getTransporter } from './getTransporter'; +import { getLogger } from './getLogger'; + +const hostIP = process.env.INSTANCE_IP ? String(process.env.INSTANCE_IP).trim() : 'localhost'; export class InstanceService extends ServiceClassInternal implements IInstanceService { protected name = 'instance'; @@ -26,7 +29,7 @@ export class InstanceService extends ServiceClassInternal implements IInstanceSe constructor() { super(); - const tx = getTransporter({ transporter: process.env.TRANSPORTER, port: process.env.TCP_PORT }); + const tx = getTransporter({ transporter: process.env.TRANSPORTER, port: process.env.TCP_PORT, extra: process.env.TRANSPORTER_EXTRA }); if (typeof tx === 'string') { this.transporter = new Transporters.NATS({ url: tx }); this.isTransporterTCP = false; @@ -37,6 +40,8 @@ export class InstanceService extends ServiceClassInternal implements IInstanceSe if (this.isTransporterTCP) { this.onEvent('watch.instanceStatus', async ({ clientAction, data }): Promise => { if (clientAction === 'removed') { + (this.broker.transit?.tx as any).nodes.disconnected(data?._id, false); + (this.broker.transit?.tx as any).nodes.nodes.delete(data?._id); return; } @@ -78,8 +83,14 @@ export class InstanceService extends ServiceClassInternal implements IInstanceSe this.broker = new ServiceBroker({ nodeID: InstanceStatus.id(), transporter: this.transporter, + + ...getLogger(process.env), }); + if ((this.broker.transit?.tx as any)?.nodes?.localNode) { + (this.broker.transit?.tx as any).nodes.localNode.ipList = [hostIP]; + } + this.broker.createService({ name: 'matrix', events: { @@ -106,7 +117,7 @@ export class InstanceService extends ServiceClassInternal implements IInstanceSe await this.broker.start(); const instance = { - host: process.env.INSTANCE_IP ? String(process.env.INSTANCE_IP).trim() : 'localhost', + host: hostIP, port: String(process.env.PORT).trim(), tcpPort: (this.broker.transit?.tx as any)?.nodes?.localNode?.port, os: { diff --git a/apps/meteor/server/modules/watchers/watchers.module.ts b/apps/meteor/server/modules/watchers/watchers.module.ts index 158c0457c865..0722d93f0b2c 100644 --- a/apps/meteor/server/modules/watchers/watchers.module.ts +++ b/apps/meteor/server/modules/watchers/watchers.module.ts @@ -301,6 +301,11 @@ export function initWatchers(watcher: DatabaseWatcher, broadcast: BroadcastCallb }); watcher.on(InstanceStatus.getCollectionName(), ({ clientAction, id, data, diff }) => { + if (clientAction === 'removed') { + void broadcast('watch.instanceStatus', { clientAction, id, data: { _id: id } }); + return; + } + void broadcast('watch.instanceStatus', { clientAction, data, diff, id }); }); From 929f45716141dcc4d49ad2afe8315492c9028f9c Mon Sep 17 00:00:00 2001 From: Debdut Chakraborty Date: Wed, 21 Jun 2023 05:35:00 +0530 Subject: [PATCH 005/149] fix(multiinstance): use ipList for node:node connection (#29472) --- .changeset/pink-carrots-matter.md | 5 +++++ .../ee/server/local-services/instance/getTransporter.ts | 1 + .../server/local-services/instance/getTransporter.spec.ts | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .changeset/pink-carrots-matter.md diff --git a/.changeset/pink-carrots-matter.md b/.changeset/pink-carrots-matter.md new file mode 100644 index 000000000000..58263961344e --- /dev/null +++ b/.changeset/pink-carrots-matter.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed ENOTFOUND error in k8s deployments diff --git a/apps/meteor/ee/server/local-services/instance/getTransporter.ts b/apps/meteor/ee/server/local-services/instance/getTransporter.ts index 747321ea0edb..8f8ebabf01f7 100644 --- a/apps/meteor/ee/server/local-services/instance/getTransporter.ts +++ b/apps/meteor/ee/server/local-services/instance/getTransporter.ts @@ -11,6 +11,7 @@ export function getTransporter({ transporter, port, extra }: { transporter?: str return { port: port ? port.trim() : 0, udpDiscovery: false, + useHostname: false, ...(extra ? JSON.parse(extra) : {}), }; } diff --git a/apps/meteor/ee/tests/unit/apps/meteor/ee/server/local-services/instance/getTransporter.spec.ts b/apps/meteor/ee/tests/unit/apps/meteor/ee/server/local-services/instance/getTransporter.spec.ts index 0fc3707cd8cc..9fed06f80095 100644 --- a/apps/meteor/ee/tests/unit/apps/meteor/ee/server/local-services/instance/getTransporter.spec.ts +++ b/apps/meteor/ee/tests/unit/apps/meteor/ee/server/local-services/instance/getTransporter.spec.ts @@ -4,15 +4,15 @@ import { getTransporter } from '../../../../../../../../server/local-services/in describe('getTransporter', () => { it('should return TCP with port 0 by default', () => { - expect(getTransporter()).to.deep.equal({ port: 0, udpDiscovery: false }); + expect(getTransporter()).to.deep.equal({ port: 0, udpDiscovery: false, useHostname: false }); }); it('should return TCP with port set via env var', () => { - expect(getTransporter({ port: '1234' })).to.deep.equal({ port: '1234', udpDiscovery: false }); + expect(getTransporter({ port: '1234' })).to.deep.equal({ port: '1234', udpDiscovery: false, useHostname: false }); - expect(getTransporter({ port: ' 1234' })).to.deep.equal({ port: '1234', udpDiscovery: false }); + expect(getTransporter({ port: ' 1234' })).to.deep.equal({ port: '1234', udpDiscovery: false, useHostname: false }); - expect(getTransporter({ port: ' 1234 ' })).to.deep.equal({ port: '1234', udpDiscovery: false }); + expect(getTransporter({ port: ' 1234 ' })).to.deep.equal({ port: '1234', udpDiscovery: false, useHostname: false }); }); it('should throw if transporter set incorrectly', () => { From 528bcfc5419d128cae95a866c62e7f92879b02e8 Mon Sep 17 00:00:00 2001 From: rocketchat-github-ci Date: Wed, 21 Jun 2023 04:07:43 +0000 Subject: [PATCH 006/149] Release 6.2.7 --- .changeset/five-clouds-obey.md | 5 ---- .changeset/pink-carrots-matter.md | 5 ---- .changeset/popular-llamas-notice.md | 5 ---- .changeset/proud-cars-guess.md | 2 -- .changeset/wet-cherries-hunt.md | 5 ---- apps/meteor/CHANGELOG.md | 27 +++++++++++++++++++ apps/meteor/ee/server/services/CHANGELOG.md | 11 ++++++++ apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/CHANGELOG.md | 11 ++++++++ ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/CHANGELOG.md | 11 ++++++++ ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/CHANGELOG.md | 12 +++++++++ ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/CHANGELOG.md | 12 +++++++++ ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/CHANGELOG.md | 11 ++++++++ ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/CHANGELOG.md | 11 ++++++++ ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/CHANGELOG.md | 10 +++++++ ee/apps/stream-hub-service/package.json | 2 +- ee/packages/omnichannel-services/CHANGELOG.md | 12 +++++++++ ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/CHANGELOG.md | 7 +++++ ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/CHANGELOG.md | 9 +++++++ ee/packages/presence/package.json | 2 +- package.json | 2 +- packages/api-client/CHANGELOG.md | 8 ++++++ packages/api-client/package.json | 2 +- packages/core-services/CHANGELOG.md | 9 +++++++ packages/core-services/package.json | 2 +- packages/core-typings/CHANGELOG.md | 2 ++ packages/core-typings/package.json | 2 +- packages/instance-status/CHANGELOG.md | 7 +++++ packages/instance-status/package.json | 2 +- packages/model-typings/CHANGELOG.md | 7 +++++ packages/model-typings/package.json | 2 +- packages/models/CHANGELOG.md | 7 +++++ packages/models/package.json | 2 +- packages/release-action/CHANGELOG.md | 8 ++++++ packages/release-action/package.json | 2 +- packages/rest-typings/CHANGELOG.md | 7 +++++ packages/rest-typings/package.json | 2 +- packages/ui-contexts/CHANGELOG.md | 8 ++++++ packages/ui-contexts/package.json | 2 +- 48 files changed, 229 insertions(+), 44 deletions(-) delete mode 100644 .changeset/five-clouds-obey.md delete mode 100644 .changeset/pink-carrots-matter.md delete mode 100644 .changeset/popular-llamas-notice.md delete mode 100644 .changeset/proud-cars-guess.md delete mode 100644 .changeset/wet-cherries-hunt.md diff --git a/.changeset/five-clouds-obey.md b/.changeset/five-clouds-obey.md deleted file mode 100644 index 2a00dc5ce031..000000000000 --- a/.changeset/five-clouds-obey.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fix: Wrong IP usage on monolith TCP transporter configuration diff --git a/.changeset/pink-carrots-matter.md b/.changeset/pink-carrots-matter.md deleted file mode 100644 index 58263961344e..000000000000 --- a/.changeset/pink-carrots-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': patch ---- - -Fixed ENOTFOUND error in k8s deployments diff --git a/.changeset/popular-llamas-notice.md b/.changeset/popular-llamas-notice.md deleted file mode 100644 index dfcc5344e01d..000000000000 --- a/.changeset/popular-llamas-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/release-action': patch ---- - -Use CI user PAT token diff --git a/.changeset/proud-cars-guess.md b/.changeset/proud-cars-guess.md deleted file mode 100644 index a845151cc840..000000000000 --- a/.changeset/proud-cars-guess.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/wet-cherries-hunt.md b/.changeset/wet-cherries-hunt.md deleted file mode 100644 index f16cd3df20d0..000000000000 --- a/.changeset/wet-cherries-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/release-action': patch ---- - -Update versions in configured files diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index 20542d3f6726..c27531b45111 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,5 +1,32 @@ # @rocket.chat/meteor +## 6.2.7 + +### Patch Changes + +- [#29596](https://github.com/RocketChat/Rocket.Chat/pull/29596) [`74983f5b46`](https://github.com/RocketChat/Rocket.Chat/commit/74983f5b46abacbde2afb0838681ef3c5023307d) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - fix: Wrong IP usage on monolith TCP transporter configuration + +- [#29596](https://github.com/RocketChat/Rocket.Chat/pull/29596) [`929f457161`](https://github.com/RocketChat/Rocket.Chat/commit/929f45716141dcc4d49ad2afe8315492c9028f9c) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - Fixed ENOTFOUND error in k8s deployments + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + - @rocket.chat/omnichannel-services@0.0.3 + - @rocket.chat/pdf-worker@0.0.3 + - @rocket.chat/presence@0.0.3 + - @rocket.chat/api-client@0.0.3 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/gazzodown@0.0.1 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/ui-contexts@0.0.3 + - @rocket.chat/models@0.0.3 + - @rocket.chat/ui-theming@0.0.1 + - @rocket.chat/fuselage-ui-kit@0.31.16 + - @rocket.chat/ui-client@0.0.1 + - @rocket.chat/ui-video-conf@0.0.1 + - @rocket.chat/web-ui-registration@0.0.1 + - @rocket.chat/instance-status@0.0.3 + ## 6.2.6 ### Patch Changes diff --git a/apps/meteor/ee/server/services/CHANGELOG.md b/apps/meteor/ee/server/services/CHANGELOG.md index c3478f72ad6d..e0e9d1ca5c11 100644 --- a/apps/meteor/ee/server/services/CHANGELOG.md +++ b/apps/meteor/ee/server/services/CHANGELOG.md @@ -1,5 +1,16 @@ # rocketchat-services +## 1.0.2 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 1.0.1 ### Patch Changes diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index bade824a564c..72ac960e626a 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -1,7 +1,7 @@ { "name": "rocketchat-services", "private": true, - "version": "1.0.1", + "version": "1.0.2", "description": "Rocket.Chat Authorization service", "main": "index.js", "scripts": { diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 7a3f36692b97..4585c2d528f9 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/meteor", "description": "The Ultimate Open Source WebChat Platform", - "version": "6.2.6", + "version": "6.2.7", "private": true, "author": { "name": "Rocket.Chat", diff --git a/ee/apps/account-service/CHANGELOG.md b/ee/apps/account-service/CHANGELOG.md index 2fc7f914c256..1fb9d208707d 100644 --- a/ee/apps/account-service/CHANGELOG.md +++ b/ee/apps/account-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/account-service +## 0.1.2 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.1.1 ### Patch Changes diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index 291bda3eded7..29f091a2115d 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/account-service", "private": true, - "version": "0.1.1", + "version": "0.1.2", "description": "Rocket.Chat Account service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/authorization-service/CHANGELOG.md b/ee/apps/authorization-service/CHANGELOG.md index 7a41f670b511..815e805c9cb3 100644 --- a/ee/apps/authorization-service/CHANGELOG.md +++ b/ee/apps/authorization-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/authorization-service +## 0.1.2 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.1.1 ### Patch Changes diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index a84a65238672..d897508cfe38 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/authorization-service", "private": true, - "version": "0.1.1", + "version": "0.1.2", "description": "Rocket.Chat Authorization service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/ddp-streamer/CHANGELOG.md b/ee/apps/ddp-streamer/CHANGELOG.md index 72bbf35e7d36..7b94c4927772 100644 --- a/ee/apps/ddp-streamer/CHANGELOG.md +++ b/ee/apps/ddp-streamer/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/ddp-streamer +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + - @rocket.chat/instance-status@0.0.3 + ## 0.0.2 ### Patch Changes diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index 25fa80aa1f43..f10c8ea3b757 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/ddp-streamer", "private": true, - "version": "0.0.2", + "version": "0.0.3", "description": "Rocket.Chat DDP-Streamer service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/omnichannel-transcript/CHANGELOG.md b/ee/apps/omnichannel-transcript/CHANGELOG.md index 5b3a2c9e84ff..9d9a9acc118b 100644 --- a/ee/apps/omnichannel-transcript/CHANGELOG.md +++ b/ee/apps/omnichannel-transcript/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/omnichannel-transcript +## 0.1.2 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/omnichannel-services@0.0.3 + - @rocket.chat/pdf-worker@0.0.3 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.1.1 ### Patch Changes diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index ce2c59420dd2..d99b89d16e42 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/omnichannel-transcript", "private": true, - "version": "0.1.1", + "version": "0.1.2", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/presence-service/CHANGELOG.md b/ee/apps/presence-service/CHANGELOG.md index 18ac16d44137..8891187ff08f 100644 --- a/ee/apps/presence-service/CHANGELOG.md +++ b/ee/apps/presence-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/presence-service +## 0.1.2 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/presence@0.0.3 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.1.1 ### Patch Changes diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index 39ee951c9498..9cccd3703f3e 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/presence-service", "private": true, - "version": "0.1.1", + "version": "0.1.2", "description": "Rocket.Chat Presence service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/queue-worker/CHANGELOG.md b/ee/apps/queue-worker/CHANGELOG.md index 2c283dd36be0..08b199f14d9c 100644 --- a/ee/apps/queue-worker/CHANGELOG.md +++ b/ee/apps/queue-worker/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/queue-worker +## 0.1.2 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/omnichannel-services@0.0.3 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.1.1 ### Patch Changes diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index f2dd7de48c4f..efdfae6cd15b 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/queue-worker", "private": true, - "version": "0.1.1", + "version": "0.1.2", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/stream-hub-service/CHANGELOG.md b/ee/apps/stream-hub-service/CHANGELOG.md index df8798a34f8d..de639f94831a 100644 --- a/ee/apps/stream-hub-service/CHANGELOG.md +++ b/ee/apps/stream-hub-service/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/stream-hub-service +## 0.1.2 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.1.1 ### Patch Changes diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index ce11d161c7e5..3d481a9160b7 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/stream-hub-service", "private": true, - "version": "0.1.1", + "version": "0.1.2", "description": "Rocket.Chat Stream Hub service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/packages/omnichannel-services/CHANGELOG.md b/ee/packages/omnichannel-services/CHANGELOG.md index d208e05c6156..4b169eff955c 100644 --- a/ee/packages/omnichannel-services/CHANGELOG.md +++ b/ee/packages/omnichannel-services/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/omnichannel-services +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + - @rocket.chat/pdf-worker@0.0.3 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/model-typings@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.0.2 ### Patch Changes diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index ad05e2cbcc98..3eefbc9cd26a 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/omnichannel-services", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/ee/packages/pdf-worker/CHANGELOG.md b/ee/packages/pdf-worker/CHANGELOG.md index 6eaa82ac514c..bf44622d6166 100644 --- a/ee/packages/pdf-worker/CHANGELOG.md +++ b/ee/packages/pdf-worker/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/pdf-worker +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + ## 0.0.2 ### Patch Changes diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index da3752e68dd5..fab3db3504c0 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/pdf-worker", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@storybook/addon-actions": "~6.5.14", diff --git a/ee/packages/presence/CHANGELOG.md b/ee/packages/presence/CHANGELOG.md index a5dee9b46a83..bc882fd8ec0f 100644 --- a/ee/packages/presence/CHANGELOG.md +++ b/ee/packages/presence/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/presence +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/core-services@0.0.3 + - @rocket.chat/models@0.0.3 + ## 0.0.2 ### Patch Changes diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 20f57c175187..6894a268f110 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/presence", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@babel/core": "^7.20.5", diff --git a/package.json b/package.json index c1da545f5d23..9dc6097cb913 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.2.6", + "version": "6.2.7", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/api-client/CHANGELOG.md b/packages/api-client/CHANGELOG.md index 2b53696dfa8a..6e3b931cfdab 100644 --- a/packages/api-client/CHANGELOG.md +++ b/packages/api-client/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/api-client +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + ## 0.0.2 ### Patch Changes diff --git a/packages/api-client/package.json b/packages/api-client/package.json index 155dc5146e91..1fad6dbde4e0 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/api-client", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/core-services/CHANGELOG.md b/packages/core-services/CHANGELOG.md index c053eb65f6df..f9f550c86fb5 100644 --- a/packages/core-services/CHANGELOG.md +++ b/packages/core-services/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/core-services +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + - @rocket.chat/models@0.0.3 + ## 0.0.2 ### Patch Changes diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 0c8f52ee9fcb..a608d182e65d 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-services", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/core-typings/CHANGELOG.md b/packages/core-typings/CHANGELOG.md index da2d32d4619b..11526ef6d5a9 100644 --- a/packages/core-typings/CHANGELOG.md +++ b/packages/core-typings/CHANGELOG.md @@ -1,3 +1,5 @@ # @rocket.chat/core-typings +## 6.2.7 + ## 6.2.6 diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index f843c714cd2e..1bb7864652da 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-typings", - "version": "6.2.6", + "version": "6.2.7", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/instance-status/CHANGELOG.md b/packages/instance-status/CHANGELOG.md index f99e52f87f34..e8726035358d 100644 --- a/packages/instance-status/CHANGELOG.md +++ b/packages/instance-status/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/instance-status +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/models@0.0.3 + ## 0.0.2 ### Patch Changes diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index b1aeac41418d..1a9cbeb3967b 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/instance-status", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/model-typings/CHANGELOG.md b/packages/model-typings/CHANGELOG.md index d23ab3911c47..71417ee3e9ef 100644 --- a/packages/model-typings/CHANGELOG.md +++ b/packages/model-typings/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/model-typings +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + ## 0.0.2 ### Patch Changes diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index b0aa963423e7..312410396b9c 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/model-typings", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index e4ce85f53cbf..87ccf5f8c057 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/models +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/model-typings@0.0.3 + ## 0.0.2 ### Patch Changes diff --git a/packages/models/package.json b/packages/models/package.json index a46abadb67ba..99909f53ec30 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/models", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/release-action/CHANGELOG.md b/packages/release-action/CHANGELOG.md index dfe6a986b528..9b1e9e82a1ff 100644 --- a/packages/release-action/CHANGELOG.md +++ b/packages/release-action/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/release-action +## 1.0.1 + +### Patch Changes + +- [#29596](https://github.com/RocketChat/Rocket.Chat/pull/29596) [`a19359b3f5`](https://github.com/RocketChat/Rocket.Chat/commit/a19359b3f5b09270fe1644df0706c6b1ebb979ef) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - Use CI user PAT token + +- [#29596](https://github.com/RocketChat/Rocket.Chat/pull/29596) [`a19359b3f5`](https://github.com/RocketChat/Rocket.Chat/commit/a19359b3f5b09270fe1644df0706c6b1ebb979ef) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - Update versions in configured files + ## 1.0.0 ### Major Changes diff --git a/packages/release-action/package.json b/packages/release-action/package.json index 72e41bb8778c..97ec14c9ed6c 100644 --- a/packages/release-action/package.json +++ b/packages/release-action/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/release-action", - "version": "1.0.0", + "version": "1.0.1", "private": true, "scripts": { "build": "tsc", diff --git a/packages/rest-typings/CHANGELOG.md b/packages/rest-typings/CHANGELOG.md index 52039671eb16..39debf6d6189 100644 --- a/packages/rest-typings/CHANGELOG.md +++ b/packages/rest-typings/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/rest-typings +## 6.2.7 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + ## 6.2.6 ### Patch Changes diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index da42419ab50b..27657acd1980 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.2.6", + "version": "6.2.7", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/ui-contexts/CHANGELOG.md b/packages/ui-contexts/CHANGELOG.md index 17d4e372f6a3..465c0fad5029 100644 --- a/packages/ui-contexts/CHANGELOG.md +++ b/packages/ui-contexts/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/ui-contexts +## 0.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.7 + - @rocket.chat/rest-typings@6.2.7 + ## 0.0.2 ### Patch Changes diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index cee517be812d..123a092e2958 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-contexts", - "version": "0.0.2", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/core-typings": "workspace:^", From 27753d300cb42fb9bd07e9f53f50591ecf21627e Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 21 Jun 2023 12:27:59 -0300 Subject: [PATCH 007/149] Release 6.2.8 --- apps/meteor/.docker/Dockerfile.rhel | 2 +- apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/package.json | 2 +- package.json | 2 +- packages/core-typings/package.json | 2 +- packages/release-action/src/publishRelease.ts | 3 ++- packages/rest-typings/package.json | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index ee56583a2f27..e3746d22c0b0 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 6.2.5 +ENV RC_VERSION 6.2.8 MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index 04a3018573ef..ab44536c9ebe 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.2.5" + "version": "6.2.8" } diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 4585c2d528f9..b756e6f7d981 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/meteor", "description": "The Ultimate Open Source WebChat Platform", - "version": "6.2.7", + "version": "6.2.8", "private": true, "author": { "name": "Rocket.Chat", diff --git a/package.json b/package.json index 9dc6097cb913..5e6cc689277d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.2.7", + "version": "6.2.8", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 1bb7864652da..ce13876bb9f7 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-typings", - "version": "6.2.7", + "version": "6.2.8", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/release-action/src/publishRelease.ts b/packages/release-action/src/publishRelease.ts index a776c9d74d26..081d38237a24 100644 --- a/packages/release-action/src/publishRelease.ts +++ b/packages/release-action/src/publishRelease.ts @@ -85,11 +85,12 @@ export async function publishRelease({ await exec('git', ['push', '--follow-tags']); - core.info('create release'); + core.info('create draft release'); await octokit.rest.repos.createRelease({ name: newVersion, tag_name: newVersion, body: releaseBody, + draft: true, prerelease: newVersion.includes('-'), ...github.context.repo, }); diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 27657acd1980..63f5bca2464b 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.2.7", + "version": "6.2.8", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", From 13142bd106cf4e9c319ee739802566f70a915cd7 Mon Sep 17 00:00:00 2001 From: AdityaSingh-02 Date: Sat, 24 Jun 2023 10:14:57 +0530 Subject: [PATCH 008/149] Added DbModel tour --- .tours/1---repository-overview.tour | 120 ++++---- .tours/2---how-a-message-is-sent.tour | 260 +++++++----------- .tours/3---how-a-message-is-sent.tour | 76 +++++ ...our => 4---how-to-create-an-endpoint.tour} | 10 +- .tours/5---how-to-create-a-db-model.tour | 35 +++ 5 files changed, 271 insertions(+), 230 deletions(-) create mode 100644 .tours/3---how-a-message-is-sent.tour rename .tours/{3---how-to-create-an-endpoint.tour => 4---how-to-create-an-endpoint.tour} (88%) create mode 100644 .tours/5---how-to-create-a-db-model.tour diff --git a/.tours/1---repository-overview.tour b/.tours/1---repository-overview.tour index 7aac4e69c3aa..5663b5631565 100644 --- a/.tours/1---repository-overview.tour +++ b/.tours/1---repository-overview.tour @@ -1,60 +1,60 @@ -{ - "$schema": "https://aka.ms/codetour-schema", - "title": "1 - Repository-Overview", - "steps": [ - { - "file": "package.json", - "description": "### 1. Welcome to the Rocket.Chat Code Tour! This tour aims to provide an overview of the *Rocket.Chat* codebase, its architecture, key features and how things are working. \n\n### 2. The code tour is designed for the Everyone, whether you're a developer looking to contribute, a student learning web development, or simply curious about Rocket.Chat's inner workings.\n\n### 3. We'll explore specific modules, functionalities, or important sections of the codebase in a structured manner, allowing you to understand how Rocket.Chat works.", - "line": 2, - "title": "Welcome" - }, - { - "directory": "packages", - "description": "### packages Folder\nThe \"package\" folder in the Rocket Chat project contains sharable code that can be used by different projects within the Rocket Chat ecosystem. It houses reusable modules, libraries, or components that follow a modular approach to code organization. The shared code is managed as a package with its own versioning system, allowing projects to depend on specific versions. The folder may include documentation, examples, tests, and quality assurance processes to ensure reliability and ease of use. By organizing sharable code in this folder, Rocket Chat promotes code reuse, modularity, and collaboration across projects.\n\nYou can also Visit [Here](https://developer.rocket.chat/open-source-projects/server/repository-structure#directory-structure) for structure" - }, - { - "directory": "ee", - "description": "### ee (Enterprice Edition) Folder\nThe \"ee\" folder in the Rocket Chat project contains code, features, and functionalities exclusive to the Enterprise Edition of Rocket Chat. It provides additional features tailored for larger organizations, such as advanced security options, compliance features, enhanced administration tools, and integrations with enterprise systems.\n\n**Additional features**: The \"ee\" folder includes code files that implement extra features and functionalities exclusive to the enterprise edition. These features are designed to meet the requirements of enterprise customers, such as advanced security options, compliance features, enhanced administration tools, integrations with enterprise systems, and more." - }, - { - "directory": "apps/meteor", - "description": "### apps/meteor Folder\n#### The \"apps/meteor\" folder is a significant part of the codebase and contains important code and imports related to the Meteor framework in the Rocket Chat project.\n\n- **Folder structure**: The \"apps/meteor\" folder may have a structured organization, with subfolders representing different aspects of the application. For example:\n - **Client**: This subfolder may contain code specific to the client-side implementation, such as UI components, templates, stylesheets, and client-side libraries.\n - **Server**: This subfolder may contain code that runs on the server-side, handling server operations like database interactions, API endpoints, and server-side functions.\n - **Lib**: This subfolder may contain reusable code and utilities that can be shared between the client and server.\n - **Methods**: This subfolder may contain code for server-side methods, which provide an interface for client-side code to interact with the server and perform operations securely.\n - **Public**: This subfolder may contain publicly accessible files, such as static assets (images, fonts, etc.) that can be served directly to clients.\n - **Private**: This subfolder may contain private files and assets that are only accessible to the server-side code.", - "title": "apps/meteor" - }, - { - "directory": "apps/meteor/client", - "description": "### client Folder\n\n\nIn Rocket Chat, the \"apps/meteor/client\" directory refers to the client-side code that is specific to the Meteor framework within the Rocket Chat application. It contains frontend-related code and resources that are responsible for rendering and handling the user interface on the client side.\n" - }, - { - "directory": "apps/meteor/client/components", - "description": "### client/components Folder\n\n#### This folder contains reusable UI components. These components are modular and can be used in different parts of the application to provide consistent and reusable user interface elements.\n\n- **Reusable UI components**: The folder houses code files that define reusable UI components, such as buttons, input fields, modals, cards, avatars, tooltips, or any other user interface element that can be utilized in multiple parts of the Rocket Chat application.\n\n\n- **Component structure**: Each component typically consists of a JavaScript or TypeScript file that contains the component's logic and functionality. It may also include associated stylesheets, templates, or configuration files specific to that component.\n\n\n- **Composition and customization**: Components can often be composed together to build more complex UI elements. Developers can leverage the components in the folder to assemble larger, composite components that cater to specific features or requirements of the Rocket Chat application. Components may also provide options for customization through props or configuration parameters.\n\n- **Consistency and UI guidelines**: The components in the \"client/components\" folder adhere to established UI guidelines and design patterns within the Rocket Chat project. They help maintain consistency in the user interface across different parts of the application and ensure a cohesive and intuitive user experience.", - "title": "Client/components" - }, - { - "directory": "apps/meteor/client/lib", - "description": "### apps/meteor/client/lib/ Folder\n\n- ***This contains Code which can be used by both Server and Client part of application.***\n\n### A collection of objects that are reused on all of the client sides.\n#### This is to:\n- **Limit code duplication**\n- **Encourage contributors to use the code that is already existing in the codebase**\n- **Avoid re-implementing logic or re-create functions**" - }, - { - "directory": "apps/meteor/client/views", - "description": "### The Best place to Start (The FrontEnd)\n\n### *If you are a beginner looking to contribute to Rocket Chat, the \"client/views\" folder is an ideal starting point. It offers a clearer understanding of the application's inner workings by tracing module imports and observing component usage. By exploring this folder, you can learn how different components are structured, styled, and interact with each other. It provides valuable insights into the UI construction, facilitating improvements and feature additions. Starting your contribution journey in the \"client/views\" folder will enhance your understanding of the codebase and enable effective contributions to the project.*\n\n- **This is the Folder where a combination of multiple components comes together in action to build a single Rocket.Chat page is seen by client-side(Frontend) users.**\n\n- **The root view here can be seen in apps/meteor/client/views/root/AppRoot.tsx [here](./apps/meteor/client/views/root/AppRoot.tsx) where execution in the front begins**\n", - "title": "Best place for beginners" - }, - { - "file": "apps/meteor/client/views/root/AppRoot.tsx", - "description": "### The Root File\n\n- Rocket Chat is the starting point for the frontend execution, Since Rocket chat's Frontend is build on React hence it has a Root from where execution Starts. It initializes the application and establishes the layout. It plays a crucial role in setting up the initial structure and behavior of the frontend.\n\n- If You want to deeply understand how things are working you can trace elements and methods used in here", - "line": 12 - }, - { - "directory": "apps/meteor/private", - "description": "### Private Directory\n\n- All files inside a top-level directory called **private/** are only accessible from server code and can be loaded via the Assets API. This can be used for private data files and any files that are in your project directory that you don’t want to be accessible from the outside." - }, - { - "directory": "apps/meteor/public", - "description": "### Public Directory\n\n- All files inside a top-level directory called **public/** are served as-is to the client.\n- It can be accessed by everyone and can be imported directly" - }, - { - "directory": "apps/meteor/server", - "description": "### Server Directory\n\n- Server/ Directory is never loaded on the client side, People usually like to call it as backend and in Rocket.chat we use **NodeJs** as backend.\n- Most of the API Endpoints are created here and stored here through Database and in Rocket.chat we use **MongoDB** as Database\n- Any sensitive code that you don’t want served to the client, such as code containing passwords or authentication mechanisms, should be kept in the server/ directory.\n- *There are more Folders/Directories named as Server, Just remember all of them are servers*" - } - ] -} +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "1 - Repository-Overview", + "steps": [ + { + "file": "package.json", + "description": "### 1. Welcome to the Rocket.Chat Code Tour! This tour aims to provide an overview of the *Rocket.Chat* codebase, its architecture, key features and how things are working. \n\n### 2. The code tour is designed for the Everyone, whether you're a developer looking to contribute, a student learning web development, or simply curious about Rocket.Chat's inner workings.\n\n### 3. We'll explore specific modules, functionalities, or important sections of the codebase in a structured manner, allowing you to understand how Rocket.Chat works.", + "line": 2, + "title": "Welcome" + }, + { + "directory": "packages", + "description": "### packages Folder\nThe \"package\" folder in the Rocket Chat project contains sharable code that can be used by different projects within the Rocket Chat ecosystem. It houses reusable modules, libraries, or components that follow a modular approach to code organization. The shared code is managed as a package with its own versioning system, allowing projects to depend on specific versions. The folder may include documentation, examples, tests, and quality assurance processes to ensure reliability and ease of use. By organizing sharable code in this folder, Rocket Chat promotes code reuse, modularity, and collaboration across projects.\n\nYou can also Visit [Here](https://developer.rocket.chat/open-source-projects/server/repository-structure#directory-structure) for structure" + }, + { + "directory": "ee", + "description": "### ee (Enterprice Edition) Folder\nThe \"ee\" folder in the Rocket Chat project contains code, features, and functionalities exclusive to the Enterprise Edition of Rocket Chat. It provides additional features tailored for larger organizations, such as advanced security options, compliance features, enhanced administration tools, and integrations with enterprise systems.\n\n**Additional features**: The \"ee\" folder includes code files that implement extra features and functionalities exclusive to the enterprise edition. These features are designed to meet the requirements of enterprise customers, such as advanced security options, compliance features, enhanced administration tools, integrations with enterprise systems, and more." + }, + { + "directory": "apps/meteor", + "description": "### apps/meteor Folder\n#### The \"apps/meteor\" folder is a significant part of the codebase and contains important code and imports related to the Meteor framework in the Rocket Chat project.\n\n- **Folder structure**: The \"apps/meteor\" folder may have a structured organization, with subfolders representing different aspects of the application. For example:\n - **Client**: This subfolder may contain code specific to the client-side implementation, such as UI components, templates, stylesheets, and client-side libraries.\n - **Server**: This subfolder may contain code that runs on the server-side, handling server operations like database interactions, API endpoints, and server-side functions.\n - **Lib**: This subfolder may contain reusable code and utilities that can be shared between the client and server.\n - **Methods**: This subfolder may contain code for server-side methods, which provide an interface for client-side code to interact with the server and perform operations securely.\n - **Public**: This subfolder may contain publicly accessible files, such as static assets (images, fonts, etc.) that can be served directly to clients.\n - **Private**: This subfolder may contain private files and assets that are only accessible to the server-side code.", + "title": "apps/meteor" + }, + { + "directory": "apps/meteor/client", + "description": "### client Folder\n\n\nIn Rocket Chat, the \"apps/meteor/client\" directory refers to the client-side code that is specific to the Meteor framework within the Rocket Chat application. It contains frontend-related code and resources that are responsible for rendering and handling the user interface on the client side.\n" + }, + { + "directory": "apps/meteor/client/components", + "description": "### client/components Folder\n\n#### This folder contains reusable UI components. These components are modular and can be used in different parts of the application to provide consistent and reusable user interface elements.\n\n- **Reusable UI components**: The folder houses code files that define reusable UI components, such as buttons, input fields, modals, cards, avatars, tooltips, or any other user interface element that can be utilized in multiple parts of the Rocket Chat application.\n\n\n- **Component structure**: Each component typically consists of a JavaScript or TypeScript file that contains the component's logic and functionality. It may also include associated stylesheets, templates, or configuration files specific to that component.\n\n\n- **Composition and customization**: Components can often be composed together to build more complex UI elements. Developers can leverage the components in the folder to assemble larger, composite components that cater to specific features or requirements of the Rocket Chat application. Components may also provide options for customization through props or configuration parameters.\n\n- **Consistency and UI guidelines**: The components in the \"client/components\" folder adhere to established UI guidelines and design patterns within the Rocket Chat project. They help maintain consistency in the user interface across different parts of the application and ensure a cohesive and intuitive user experience.", + "title": "Client/components" + }, + { + "directory": "apps/meteor/client/lib", + "description": "### apps/meteor/client/lib/ Folder\n\n- ***This contains Code which can be used by both Server and Client part of application.***\n\n### A collection of objects that are reused on all of the client sides.\n#### This is to:\n- **Limit code duplication**\n- **Encourage contributors to use the code that is already existing in the codebase**\n- **Avoid re-implementing logic or re-create functions**" + }, + { + "directory": "apps/meteor/client/views", + "description": "### The Best place to Start (The FrontEnd)\n\n### *If you are a beginner looking to contribute to Rocket Chat, the \"client/views\" folder is an ideal starting point. It offers a clearer understanding of the application's inner workings by tracing module imports and observing component usage. By exploring this folder, you can learn how different components are structured, styled, and interact with each other. It provides valuable insights into the UI construction, facilitating improvements and feature additions. Starting your contribution journey in the \"client/views\" folder will enhance your understanding of the codebase and enable effective contributions to the project.*\n\n- **This is the Folder where a combination of multiple components comes together in action to build a single Rocket.Chat page is seen by client-side(Frontend) users.**\n\n- **The root view here can be seen in apps/meteor/client/views/root/AppRoot.tsx [here](./apps/meteor/client/views/root/AppRoot.tsx) where execution in the front begins**\n", + "title": "Best place for beginners" + }, + { + "file": "apps/meteor/client/views/root/AppRoot.tsx", + "description": "### The Root File\n\n- Rocket Chat is the starting point for the frontend execution, Since Rocket chat's Frontend is build on React hence it has a Root from where execution Starts. It initializes the application and establishes the layout. It plays a crucial role in setting up the initial structure and behavior of the frontend.\n\n- If You want to deeply understand how things are working you can trace elements and methods used in here", + "line": 12 + }, + { + "directory": "apps/meteor/private", + "description": "### Private Directory\n\n- All files inside a top-level directory called **private/** are only accessible from server code and can be loaded via the Assets API. This can be used for private data files and any files that are in your project directory that you don’t want to be accessible from the outside." + }, + { + "directory": "apps/meteor/public", + "description": "### Public Directory\n\n- All files inside a top-level directory called **public/** are served as-is to the client.\n- It can be accessed by everyone and can be imported directly" + }, + { + "directory": "apps/meteor/server", + "description": "### Server Directory\n\n- Server/ Directory is never loaded on the client side, People usually like to call it as backend and in Rocket.chat we use **NodeJs** as backend.\n- Most of the API Endpoints are created here and stored here through Database and in Rocket.chat we use **MongoDB** as Database\n- Any sensitive code that you don’t want served to the client, such as code containing passwords or authentication mechanisms, should be kept in the server/ directory.\n- *There are more Folders/Directories named as Server, Just remember all of them are servers*" + } + ] +} \ No newline at end of file diff --git a/.tours/2---how-a-message-is-sent.tour b/.tours/2---how-a-message-is-sent.tour index 10833401ef6d..64f692ce3833 100644 --- a/.tours/2---how-a-message-is-sent.tour +++ b/.tours/2---how-a-message-is-sent.tour @@ -1,166 +1,96 @@ -{ - "$schema": "https://aka.ms/codetour-schema", - "title": "2 - How a Message is sent", - "steps": [ - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "## How a Message is Sent\n\n### *In this guide, we will explore the protocols, technologies, and tools involved in message sending. By understanding the fundamentals, you will gain confidence in navigating through the repository, Hence you will get more precise understanding of how a message is sent and what are methods being used, how are API calls made and much more. Let's embark on this exciting journey together to uncover the wonders of message sending!*", - "line": 1 - }, - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "## The Chat Room/Channel\n\n- **RoomBody.tsx** is a file that handles the rendering of the Chat Room/Channel on the client side. It brings together multiple components to enable user interaction and communication within the chat environment.\n\n- In this file, there are various methods implemented. Let's specifically examine the process of sending a message through the **MessageComposer** component.\n#### **Additionally, the code in \"RoomBody.tsx\" includes logic for performing various actions related to the chat functionality. These actions might include *sending messages*, *editing* or *deleting* messages, *managing user permissions*, and handling user interactions within the chat environment.**", - "line": 53 - }, - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "### Chat/Room Layout\n\n- The \"RoomBody.tsx\" file is straightforward to comprehend. You can easily navigate through the components and grasp their functionalities.\n\n- The majority of the code in this file revolves around message rendering and handling various chat-related actions.", - "line": 539, - "selection": { - "start": { - "line": 464, - "character": 15 - }, - "end": { - "line": 464, - "character": 19 - } - } - }, - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "### Composer Container\n\n- At the bottom of the \"RoomBody.tsx\" file, there is a component responsible for rendering the Message Composer. It accepts several props, including the room ID and subscription details. These props help verify whether the user is allowed to send messages in the given context.\n\n- The Message Composer component also manages the size and layout of the composer, allowing users to comfortably compose and send messages. It provides functionality to retrieve the previous and next messages for reference or navigation purposes.\n\n```\n \n```\n\n- **Note** ComposerContainer is also built from combination multiple components, we will explore components and see how data is sent.\n", - "line": 630 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", - "description": "### The Composer Container\n\n- In the \"ComposerContainer\" component, different composers are rendered based on specific conditions:\n\n- For omnichannel rooms, the component renders the \"ComposerOmnichannel\" composer.\n - If the chat room is a VoIP room, the component renders the \"ComposerVoIP\" composer.\n - In the case of federated rooms, the component renders the \"ComposerFederation\" composer.\n - For anonymous users, the component renders the \"ComposerAnonymous\" composer.\n - If the chat room is read-only, the component renders the \"ComposerReadOnly\" composer.\n - Lastly, if there are any block-related restrictions, such as blocked users or blocking others, the component renders the \"ComposerBlocked\" composer.\n\n **1. Omnichannel**\n ```\n \n ```\n **2. VoIp**\n ```\n \n ```\n **3. Federation**\n ```\n \n ```\n **4. Anonymous Users**\n ```\n \n ```\n **5. Read Only**\n ```\n \n ```\n **6. Composer Blocked**\n ```\n \n ```", - "line": 19 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", - "description": "## ComposerMessage Component\n### And at the end we have ComposerMessage which is responsible for rendering MessageBox(The Text composer at the footer of Channel/Room)", - "line": 79 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", - "description": "## MessageBox Component\n\n- At the bottom of the code, there is a \"MessageBox\" component, which represents the actual chat message box. Its presence is crucial for the proper functioning of the Composer. If you were to comment out or remove the \"MessageBox\" component and run the server, you would observe that the Composer functionality would no longer be available.\n\n- The \"MessageBox\" component plays a pivotal role in enabling users to compose and send messages within the chat interface. It provides the necessary user interface elements, such as input fields and buttons, to facilitate message composition and submission.", - "line": 80 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## The Message Box\n\n#### This file serves as the implementation of the Message Composer component, where you can explore and analyze how the code functions. By examining the code in this file, you can gain a better understanding of its inner workings.\n\n\n#### The Message Composer component receives several props, including *\"rid\"* (room ID), *\"tmid\"* (thread ID), and *\"onSend\"* (handler for sending messages), among others. These props provide necessary data and functionality for the composer to operate effectively.\n\n\n#### By inspecting and experimenting with the code, you can gain insights into how different aspects of the Message Composer are implemented and how they interact with other components and functions.\n\n- The MessageBox is using MessageBoxProps type for defining its type\n```\ntype MessageBoxProps = {rid: IRoom['_id']; ...};\nconst MessageBox = ({rid, tmid, ...}: MessageBoxProps): ReactElement => \n```", - "line": 99 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## Sending Message\n\n#### Rocket chat's Room/channel message composers have multiple options such as uploading files, Writing text messages and Quoted messages, And then we have a send button at the right corner of composer which sends what you have entered.\n\n#### To understand how the send button works for sending a simple text message:\n\n- When the send button is clicked, an event is triggered.\n- The event handler associated with the send button retrieves the text message entered by the user.\n- The text message is then processed and prepared for sending.\n- The necessary information, such as the room/channel ID, sender details, and the text message content, is included.\n- The prepared message is sent to the server via an appropriate network request or function call.\n- The server receives the message and performs further processing, including broadcasting it to the relevant recipients in the room/channel.\nThe message is displayed in the chat interface for all participants to view.", - "line": 345, - "selection": { - "start": { - "line": 3, - "character": 1 - }, - "end": { - "line": 4, - "character": 1 - } - } - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## Message Input \n\n- The MessageComposerInput component plays a crucial role in handling user inputs within the message composer. It is responsible for capturing various types of inputs and encompasses multiple associated actions.\n\n#### The MessageComposerInput component enables users to interact with the message composer through different actions, such as:\n\n1. **Typing and Editing**: Users can enter and edit text within the composer input field.\n\n2. **Formatting**: It may support various formatting options like bold, italic, bullet points, etc., allowing users to apply formatting to their messages.\n\n3. **Mentions**: Users can mention specific individuals or groups within the message by using the appropriate syntax or by selecting them from a list.\n\n4. **Emojis**: It may provide an emoji picker or support emoji shorthand, allowing users to insert emojis into their messages.\n\n5. **Attachments**: Users can attach files or media to their messages, such as images, documents, or videos.", - "line": 380 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "### The Send Button\n\n- To send any message you need to click send button or hit Enter/Return key, when you click send button **handleSendMessage** method/function is called.\n```\n \n```", - "line": 428 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## Handle message send\n\n- This is the function which sends messages further, it Gets text from chat.composer.text where chat is actully using useChat() which is coming from [ChatAPI](./apps/meteor/client/lib/chats/ChatAPI.ts) consisting of multiple functions.\n\n#### The handleSendMessage function is responsible for processing and sending the message to the intended recipients. It performs the following actions:\n\n1. Retrieves the content of the message entered by the user.\n2. Collects additional information needed for sending the message, such as the room/channel ID, sender details, and any associated metadata.\n3. Packages the message data into a suitable format for transmission.\n4. Initiates the process of sending the message, it goes through a WebSocket connection.\n5. The message is then transmitted to the server for further processing.\n6. The server handles the message, ensuring it is delivered to the specified room/channel and received by the intended recipients.\n7. Once the message is successfully sent, it may trigger updates to the chat interface, including displaying the sent message in the conversation history.\n\n- Here we have \n ```\n const chat = useChat(); // useChat is a Context using ChatAPI.\n const text = chat.composer?.text ?? ''; //Text is getting information from useChat Context.\n\n onSend?.({ // onSend was recieved as prop and hence we are returning with some value and a boolean tshow\n\t\t\tvalue: text,\n\t\t\ttshow,\n\t\t});\n ```\n", - "line": 159 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", - "description": "## The onSend Prop\n\n#### In the MessageBox component, the composerProps are passed, and we receive several methods back, including the onSend method. The onSend method takes a value and additional parameters such as tshow, and it returns a promise.\n\n#### Additionally, we utilize the useChat() hook to access various methods available in the ChatAPI. This allows us to perform actions related to chat functionality.\n\n#### By using the onSend method, we can trigger the sending of a message with the provided value. The tshow parameter might be used to display notifications or toasts related to the message sending process. The returned promise can be used to handle any asynchronous operations or to track the status of the message sending.\n\n#### The useChat() hook provides access to methods like sendMessage() or other chat-related functions. These methods can be utilized to perform various actions within the chat interface, such as sending, retrieving, or deleting messages. \n\n```\nawait chat?.action.stop('typing');\n const newMessageSent = await chat?.flows.sendMessage({ // Here the Text is sent to chat.flows.sendMessage \n\t text,\n\t tshow,\n });\n\tif (newMessageSent) onSend?.();\n```", - "line": 27 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", - "description": "## onSend Function\n\n#### The onSend function sends a new message by calling the chat?.flows.sendMessage() method from useChat Context. It passes the value and tshow parameters as properties of an object and awaits the promise to resolve. The resolved value is stored in the newMessageSent variable.\n\n```\n const newMessageSent = await chat?.flows.sendMessage({ // Here chat.flows.sendMessage() is actually a property comming from ChatAPI. From here we are simply sending value to sendMessage property in ChatAPI\n text,\n tshow,\n\t});\n```\n", - "line": 42 - }, - { - "file": "apps/meteor/client/lib/chats/ChatAPI.ts", - "description": "## The ChatAPI\n\n- **ComposerAPI:** This interface defines methods and properties related to the message composer functionality, such as managing text input, handling selection, wrapping text, inserting text, clearing, focusing, and more. It also includes properties for managing quoted messages, editing mode, recording mode, and microphone permissions.\n\n- **DataAPI:** This interface defines methods for interacting with message and room data. It includes functions for composing and managing messages, finding and getting messages by ID, finding the last and previous own messages, managing drafts, accessing room and subscription information, marking rooms as read, and more.\n\n- **UploadsAPI:** This interface defines methods and properties for managing file uploads. It includes functions for retrieving the list of uploads, subscribing to changes, canceling and wiping failed uploads, and sending files.\n\n- **ChatAPI:** This interface represents the main API object for the Rocket Chat client. It includes properties for user-related information, such as the user ID. It also provides access to the composer, data, uploads, and message editing functionality. Additionally, it exposes methods for performing actions like starting/stopping typing, opening/closing user cards and emoji pickers, and handling various message-related flows (uploading files, sending messages, processing slash commands, editing messages, setting reactions, etc.).", - "line": 106 - }, - { - "file": "apps/meteor/client/lib/chats/ChatAPI.ts", - "description": "## ChatAPI sendMessage\n\n#### Among many properties and functions present in ChatAPI here we have sendMessage.\n\n#### Note that sendMessage is *readonly* implying that it cannot be modified after initialization. Hence Implementing ChatAPI in any class would make it modification possible.\n\n```\n readonly sendMessage: ({ text, tshow }: { text: string; tshow?: boolean }) => Promise;\n```", - "line": 146 - }, - { - "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", - "description": "## Implementation of ChatAPI\n\n#### Here *class ChatMessages implements ChatAPI* and above it we have a *type DeepWritable* which removes readonly property from ChatAPI and This allows us to modify those properties\n", - "line": 27 - }, - { - "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", - "description": "## SendMessage -\n\n### Above we have a constructor and here in sendMessage we are calling *sendMessage.bind(this,this)* Here sendMessage is a function which makes the meteor call for sending Message Data.", - "line": 160 - }, - { - "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", - "description": "## sendMessage function\n\n### The sendMessage recieves text: string and tshow: boolean properties and returns a Promise, And note that here we are utilizing ChatAPI again.\n\n- Within the sendMessage implementation, there are several checks and actions performed based on the provided parameters and the current state of the chat. These checks ensure that the message is valid and that the necessary actions are taken. The implementation of sendMessage may involve interacting with other methods or properties defined in the ChatAPI interface or its implementation.\n\n- By carefully examining the code and understanding its logic, you can gain a better understanding of how the sendMessage function is modified and what actions it performs in the context of the ChatMessages class.\n\n", - "line": 35, - "selection": { - "start": { - "line": 3, - "character": 5 - }, - "end": { - "line": 3, - "character": 258 - } - } - }, - { - "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", - "description": "## Processing Message Further\n\n### In Rocket.chat Messages are sent to ../api/v1/method.call/sendMessage and this is a meteor method. ", - "line": 62 - }, - { - "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", - "description": "## Calling Meteor method\n\n### This method is taking 2 parameters, one is the route for meteor method(api/v1/method.call/:method) and another parameter is message which is passed on by sendMessage function below.\n\n```\nawait sdk.call('sendMessage', message);\n```", - "line": 32 - }, - { - "file": "apps/meteor/app/utils/client/lib/SDKClient.ts", - "description": "## Meteor.Call\n\n### The sendMessage function in the code triggers a Meteor method by passing a method name and its corresponding parameters. This invocation is responsible for initiating the sendMessage functionality. If by any chance someone calls any other method it would simply throw error.\n\n- There are basically **2 Meteor methods** for sendMessage each of them performing different tasks\n - **The first method is responsible for sending the message data to the database and performing various related tasks. However, there may be a slight delay in the message being rendered in the room or channel. To mitigate this delay, a second Meteor method is executed concurrently in an asynchronous manner.**\n\n - **The second method is specifically designed to facilitate the instant display of the sent message. If this method were disabled or not implemented, there would be a noticeable delay in the message being rendered and visible to the users.**", - "line": 161 - }, - { - "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", - "description": "## Meteor.methods\n\n### In this code, we have defined a ServerMethods interface, which includes a route for the sendMessage method. When the sendMessage action is triggered, an API call is made to the route *api/v1/method.call/sendMessage*. ->\n```\nreturn executeSendMessage(uid, message)\n```\n\n#### This triggers a function *executeSendMessage()* below in a try block which is further responsible for operations", - "line": 110 - }, - { - "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", - "description": "## executeSendMessage\n\n#### - Inside the executeSendMessage() function, we receive the uid (user ID) and message as parameters. This function performs various operations related to the sendMessage action.\n\n#### - One of the key operations is triggering the sendMessage() function, which takes the user, message, room, and a boolean value as parameters. This function is responsible for handling the process of sending the message. It may involve additional validations, processing the message content, interacting with the database, and performing any necessary actions related to sending messages.", - "line": 85 - }, - { - "file": "apps/meteor/app/lib/server/functions/sendMessage.js", - "description": "## Sending message to DB\n\n### In the sendMessage.js file, The sendMessage function that is called from the [executeSendMessage()](./apps/meteor/app/lib/server/methods/sendMessage.ts) function. The purpose of the sendMessage function is to handle the actual sending of messages.\n\n#### Within the sendMessage function, there are several important steps:\n\n1. **Validations**: The function performs validations on the message, ensuring that it meets certain criteria or conditions before proceeding. This helps maintain message integrity and prevents any invalid or malicious content from being sent.\n\n2. **URL Parsing**: The function also includes a step to parse URLs within the message for safety purposes. This ensures that any URLs included in the message are properly handled and do not pose a security risk.\n\n3. **Asynchronous Callback**: The function utilizes an asynchronous callback mechanism, which allows it to run asynchronously and not block the execution flow. This is especially useful when dealing with tasks that may involve network requests or other asynchronous operations.\n\n4. **IMessage Object**: The callback returns an IMessage object, which represents the message being sent. This object may contain various properties and metadata associated with the message.\n\n6. **Storage in Database**: Once the message has passed all validations, it is stored in the database. This ensures that the message is persisted and can be retrieved or displayed at a later time.\n\n#### Finally, the function returns the message object, indicating the successful completion of the sending process.\n- You can see code below it's easy to understand and try to play around with them, Best way to understand working of any code is to make changes in them and undestand.", - "line": 206 - }, - { - "file": "apps/meteor/app/lib/client/methods/sendMessage.ts", - "description": "## The Async sendMessage Api call\n\n#### - As mentioned earlier, there are two Meteor methods for handling the sendMessage functionality and making API Call. The first method handles processing and validation of the message, while the second method addresses the delay in rendering the message on the frontend.\n\n#### - The second Meteor method is designed to ensure instant display of the sent message. When the first method completes successfully, it triggers the execution of the second method in an asynchronous manner. This allows for near-real-time rendering of the message on the user interface, reducing any noticeable delay.\n\n#### And in the end there is a similar callback as it was in previous step which helps in message rendering, The callback return IMessage object which is displayed to user\n\n#### - By separating these two methods, the application can provide a smooth and responsive user experience. The initial processing and validation are performed without blocking the UI, and once that is completed, the message is promptly displayed to the user.\n\n### You Have now successfully sent Message from composer, Note that there is one more API to sendMessage Which is an REST API, Which is currently not being used, if it would be used in future this tour would be updated. Still you can manually sendMessage by making call here [RestAPI Docs](https://developer.rocket.chat/reference/api/rest-api/endpoints/chat-endpoints/send-message) using -curl command, you can go thorugh the DOCS and try sending message on your own", - "line": 14 - } - ] +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "2 - How a Message is sent (Client side)", + "steps": [ + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "## How a Message is Sent\n\n### *In this guide, we will explore the protocols, technologies, and tools involved in message sending. By understanding the fundamentals, you will gain confidence in navigating through the repository, Hence you will get more precise understanding of how a message is sent and what are methods being used, how are API calls made and much more. Let's embark on this exciting journey together to uncover the wonders of message sending!*", + "line": 1 + }, + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "## The Chat Room/Channel\n\n- **RoomBody.tsx** is a file that handles the rendering of the Chat Room/Channel on the client side. It brings together multiple components to enable user interaction and communication within the chat environment.\n\n- In this file, there are various methods implemented. Let's specifically examine the process of sending a message through the **MessageComposer** component.\n#### **Additionally, the code in \"RoomBody.tsx\" includes logic for performing various actions related to the chat functionality. These actions might include *sending messages*, *editing* or *deleting* messages, *managing user permissions*, and handling user interactions within the chat environment.**", + "line": 53 + }, + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "### Chat/Room Layout\n\n- The \"RoomBody.tsx\" file is straightforward to comprehend. You can easily navigate through the components and grasp their functionalities.\n\n- The majority of the code in this file revolves around message rendering and handling various chat-related actions.", + "line": 539, + "selection": { + "start": { + "line": 464, + "character": 15 + }, + "end": { + "line": 464, + "character": 19 + } + } + }, + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "### Composer Container\n\n- At the bottom of the \"RoomBody.tsx\" file, there is a component responsible for rendering the Message Composer. It accepts several props, including the room ID and subscription details. These props help verify whether the user is allowed to send messages in the given context.\n\n- The Message Composer component also manages the size and layout of the composer, allowing users to comfortably compose and send messages. It provides functionality to retrieve the previous and next messages for reference or navigation purposes.\n\n```\n \n```\n\n- **Note** ComposerContainer is also built from combination multiple components, we will explore components and see how data is sent.\n", + "line": 630 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", + "description": "### The Composer Container\n\n- In the \"ComposerContainer\" component, different composers are rendered based on specific conditions:\n\n- For omnichannel rooms, the component renders the \"ComposerOmnichannel\" composer.\n - If the chat room is a VoIP room, the component renders the \"ComposerVoIP\" composer.\n - In the case of federated rooms, the component renders the \"ComposerFederation\" composer.\n - For anonymous users, the component renders the \"ComposerAnonymous\" composer.\n - If the chat room is read-only, the component renders the \"ComposerReadOnly\" composer.\n - Lastly, if there are any block-related restrictions, such as blocked users or blocking others, the component renders the \"ComposerBlocked\" composer.\n\n **1. Omnichannel**\n ```\n \n ```\n **2. VoIp**\n ```\n \n ```\n **3. Federation**\n ```\n \n ```\n **4. Anonymous Users**\n ```\n \n ```\n **5. Read Only**\n ```\n \n ```\n **6. Composer Blocked**\n ```\n \n ```", + "line": 19 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", + "description": "## ComposerMessage Component\n### And at the end we have ComposerMessage which is responsible for rendering MessageBox(The Text composer at the footer of Channel/Room)", + "line": 79 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", + "description": "## MessageBox Component\n\n- At the bottom of the code, there is a \"MessageBox\" component, which represents the actual chat message box. Its presence is crucial for the proper functioning of the Composer. If you were to comment out or remove the \"MessageBox\" component and run the server, you would observe that the Composer functionality would no longer be available.\n\n- The \"MessageBox\" component plays a pivotal role in enabling users to compose and send messages within the chat interface. It provides the necessary user interface elements, such as input fields and buttons, to facilitate message composition and submission.", + "line": 80 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## The Message Box\n\n#### This file serves as the implementation of the Message Composer component, where you can explore and analyze how the code functions. By examining the code in this file, you can gain a better understanding of its inner workings.\n\n\n#### The Message Composer component receives several props, including *\"rid\"* (room ID), *\"tmid\"* (thread ID), and *\"onSend\"* (handler for sending messages), among others. These props provide necessary data and functionality for the composer to operate effectively.\n\n\n#### By inspecting and experimenting with the code, you can gain insights into how different aspects of the Message Composer are implemented and how they interact with other components and functions.\n\n- The MessageBox is using MessageBoxProps type for defining its type\n```\ntype MessageBoxProps = {rid: IRoom['_id']; ...};\nconst MessageBox = ({rid, tmid, ...}: MessageBoxProps): ReactElement => \n```", + "line": 99 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## Sending Message\n\n#### Rocket chat's Room/channel message composers have multiple options such as uploading files, Writing text messages and Quoted messages, And then we have a send button at the right corner of composer which sends what you have entered.\n\n#### To understand how the send button works for sending a simple text message:\n\n- When the send button is clicked, an event is triggered.\n- The event handler associated with the send button retrieves the text message entered by the user.\n- The text message is then processed and prepared for sending.\n- The necessary information, such as the room/channel ID, sender details, and the text message content, is included.\n- The prepared message is sent to the server via an appropriate network request or function call.\n- The server receives the message and performs further processing, including broadcasting it to the relevant recipients in the room/channel.\nThe message is displayed in the chat interface for all participants to view.", + "line": 345, + "selection": { + "start": { + "line": 3, + "character": 1 + }, + "end": { + "line": 4, + "character": 1 + } + } + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## Message Input \n\n- The MessageComposerInput component plays a crucial role in handling user inputs within the message composer. It is responsible for capturing various types of inputs and encompasses multiple associated actions.\n\n#### The MessageComposerInput component enables users to interact with the message composer through different actions, such as:\n\n1. **Typing and Editing**: Users can enter and edit text within the composer input field.\n\n2. **Formatting**: It may support various formatting options like bold, italic, bullet points, etc., allowing users to apply formatting to their messages.\n\n3. **Mentions**: Users can mention specific individuals or groups within the message by using the appropriate syntax or by selecting them from a list.\n\n4. **Emojis**: It may provide an emoji picker or support emoji shorthand, allowing users to insert emojis into their messages.\n\n5. **Attachments**: Users can attach files or media to their messages, such as images, documents, or videos.", + "line": 380 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "### The Send Button\n\n- To send any message you need to click send button or hit Enter/Return key, when you click send button **handleSendMessage** method/function is called.\n```\n \n```", + "line": 428 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## Handle message send\n\n- This is the function which sends messages further, it Gets text from chat.composer.text where chat is actully using useChat() which is coming from [ChatAPI](./apps/meteor/client/lib/chats/ChatAPI.ts) consisting of multiple functions.\n\n#### The handleSendMessage function is responsible for processing and sending the message to the intended recipients. It performs the following actions:\n\n1. Retrieves the content of the message entered by the user.\n2. Collects additional information needed for sending the message, such as the room/channel ID, sender details, and any associated metadata.\n3. Packages the message data into a suitable format for transmission.\n4. Initiates the process of sending the message, it goes through a WebSocket connection.\n5. The message is then transmitted to the server for further processing.\n6. The server handles the message, ensuring it is delivered to the specified room/channel and received by the intended recipients.\n7. Once the message is successfully sent, it may trigger updates to the chat interface, including displaying the sent message in the conversation history.\n\n- Here we have \n ```\n const chat = useChat(); // useChat is a Context using ChatAPI.\n const text = chat.composer?.text ?? ''; //Text is getting information from useChat Context.\n\n onSend?.({ // onSend was recieved as prop and hence we are returning with some value and a boolean tshow\n\t\t\tvalue: text,\n\t\t\ttshow,\n\t\t});\n ```\n", + "line": 159 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", + "description": "## The onSend Prop\n\n#### In the MessageBox component, the composerProps are passed, and we receive several methods back, including the onSend method. The onSend method takes a value and additional parameters such as tshow, and it returns a promise.\n\n#### Additionally, we utilize the useChat() hook to access various methods available in the ChatAPI. This allows us to perform actions related to chat functionality.\n\n#### By using the onSend method, we can trigger the sending of a message with the provided value. The tshow parameter might be used to display notifications or toasts related to the message sending process. The returned promise can be used to handle any asynchronous operations or to track the status of the message sending.\n\n#### The useChat() hook provides access to methods like sendMessage() or other chat-related functions. These methods can be utilized to perform various actions within the chat interface, such as sending, retrieving, or deleting messages. \n\n```\nawait chat?.action.stop('typing');\n const newMessageSent = await chat?.flows.sendMessage({ // Here the Text is sent to chat.flows.sendMessage \n\t text,\n\t tshow,\n });\n\tif (newMessageSent) onSend?.();\n```", + "line": 27 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", + "description": "## onSend Function\n\n#### The onSend function sends a new message by calling the chat?.flows.sendMessage() method from useChat Context. It passes the value and tshow parameters as properties of an object and awaits the promise to resolve. The resolved value is stored in the newMessageSent variable.\n\n```\n const newMessageSent = await chat?.flows.sendMessage({ // Here chat.flows.sendMessage() is actually a property comming from ChatAPI. From here we are simply sending value to sendMessage property in ChatAPI\n text,\n tshow,\n\t});\n```\n", + "line": 42 + } + ] } \ No newline at end of file diff --git a/.tours/3---how-a-message-is-sent.tour b/.tours/3---how-a-message-is-sent.tour new file mode 100644 index 000000000000..7b1da92b652f --- /dev/null +++ b/.tours/3---how-a-message-is-sent.tour @@ -0,0 +1,76 @@ +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "3 - How a Message is sent (The Backend)", + "steps": [ + { + "file": "apps/meteor/client/lib/chats/ChatAPI.ts", + "description": "## The ChatAPI\n\n- **ComposerAPI:** This interface defines methods and properties related to the message composer functionality, such as managing text input, handling selection, wrapping text, inserting text, clearing, focusing, and more. It also includes properties for managing quoted messages, editing mode, recording mode, and microphone permissions.\n\n- **DataAPI:** This interface defines methods for interacting with message and room data. It includes functions for composing and managing messages, finding and getting messages by ID, finding the last and previous own messages, managing drafts, accessing room and subscription information, marking rooms as read, and more.\n\n- **UploadsAPI:** This interface defines methods and properties for managing file uploads. It includes functions for retrieving the list of uploads, subscribing to changes, canceling and wiping failed uploads, and sending files.\n\n- **ChatAPI:** This interface represents the main API object for the Rocket Chat client. It includes properties for user-related information, such as the user ID. It also provides access to the composer, data, uploads, and message editing functionality. Additionally, it exposes methods for performing actions like starting/stopping typing, opening/closing user cards and emoji pickers, and handling various message-related flows (uploading files, sending messages, processing slash commands, editing messages, setting reactions, etc.).", + "line": 106 + }, + { + "file": "apps/meteor/client/lib/chats/ChatAPI.ts", + "description": "## ChatAPI sendMessage\n\n#### Among many properties and functions present in ChatAPI here we have sendMessage.\n\n#### Note that sendMessage is *readonly* implying that it cannot be modified after initialization. Hence Implementing ChatAPI in any class would make it modification possible.\n\n```\n readonly sendMessage: ({ text, tshow }: { text: string; tshow?: boolean }) => Promise;\n```", + "line": 146 + }, + { + "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", + "description": "## Implementation of ChatAPI\n\n#### Here *class ChatMessages implements ChatAPI* and above it we have a *type DeepWritable* which removes readonly property from ChatAPI and This allows us to modify those properties\n", + "line": 27 + }, + { + "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", + "description": "## SendMessage -\n\n### Above we have a constructor and here in this.flows we are calling *sendMessage.bind(this,this)* The sendMessage is a function which further makes the meteor call for sending Message Data.", + "line": 160 + }, + { + "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", + "description": "## sendMessage function\n\n### The sendMessage recieves text: string and tshow: boolean properties and returns a Promise, And note that here we are utilizing ChatAPI again.\n\n- Within the sendMessage implementation, there are several checks and actions performed based on the provided parameters and the current state of the chat. These checks ensure that the message is valid and that the necessary actions are taken. The implementation of sendMessage may involve interacting with other methods or properties defined in the ChatAPI interface or its implementation.\n\n- By carefully examining the code and understanding its logic, you can gain a better understanding of how the sendMessage function is modified and what actions it performs in the context of the ChatMessages class.\n\n", + "line": 35, + "selection": { + "start": { + "line": 3, + "character": 5 + }, + "end": { + "line": 3, + "character": 258 + } + } + }, + { + "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", + "description": "## Processing Message Further\n\n### In Rocket.chat Messages are sent to ../api/v1/method.call/sendMessage and this is a meteor method. ", + "line": 62 + }, + { + "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", + "description": "## Calling Meteor method\n\n### This method is taking 2 parameters, one is the route for meteor method(api/v1/method.call/:method) and another parameter is message which is passed on by sendMessage function below.\n\n```\nawait sdk.call('sendMessage', message);\n```", + "line": 32 + }, + { + "file": "apps/meteor/app/utils/client/lib/SDKClient.ts", + "description": "## Meteor.Call\n\n### The sendMessage function in the code triggers a Meteor method by passing a method name and its corresponding parameters. This invocation is responsible for initiating the sendMessage functionality. If by any chance someone calls any other method it would simply throw error.\n\n- There are basically **2 Meteor methods** for sendMessage each of them performing different tasks\n - **The first method is responsible for sending the message data to the database and performing various related tasks. However, there may be a slight delay in the message being rendered in the room or channel. To mitigate this delay, a second Meteor method is executed concurrently in an asynchronous manner.**\n\n - **The second method is specifically designed to facilitate the instant display of the sent message. If this method were disabled or not implemented, there would be a noticeable delay in the message being rendered and visible to the users.**", + "line": 161 + }, + { + "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", + "description": "## Meteor.methods\n\n### In this code, we have defined a ServerMethods interface, which includes a route for the sendMessage method. When the sendMessage action is triggered, an API call is made to the route *api/v1/method.call/sendMessage*. ->\n```\nreturn executeSendMessage(uid, message)\n```\n\n#### This triggers a function *executeSendMessage()* below in a try block which is further responsible for operations", + "line": 110 + }, + { + "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", + "description": "## executeSendMessage\n\n#### - Inside the executeSendMessage() function, we receive the uid (user ID) and message as parameters. This function performs various operations related to the sendMessage action.\n\n#### - One of the key operations is triggering the sendMessage() function, which takes the user, message, room, and a boolean value as parameters. This function is responsible for handling the process of sending the message. It may involve additional validations, processing the message content, interacting with the database, and performing any necessary actions related to sending messages.", + "line": 85 + }, + { + "file": "apps/meteor/app/lib/server/functions/sendMessage.js", + "description": "## Sending message to DB\n\n### In the sendMessage.js file, The sendMessage function that is called from the [executeSendMessage()](./apps/meteor/app/lib/server/methods/sendMessage.ts) function. The purpose of the sendMessage function is to handle the actual sending of messages.\n\n#### Within the sendMessage function, there are several important steps:\n\n1. **Validations**: The function performs validations on the message, ensuring that it meets certain criteria or conditions before proceeding. This helps maintain message integrity and prevents any invalid or malicious content from being sent.\n\n2. **URL Parsing**: The function also includes a step to parse URLs within the message for safety purposes. This ensures that any URLs included in the message are properly handled and do not pose a security risk.\n\n3. **Asynchronous Callback**: The function utilizes an asynchronous callback mechanism, which allows it to run asynchronously and not block the execution flow. This is especially useful when dealing with tasks that may involve network requests or other asynchronous operations.\n\n4. **IMessage Object**: The callback returns an IMessage object, which represents the message being sent. This object may contain various properties and metadata associated with the message.\n\n6. **Storage in Database**: Once the message has passed all validations, it is stored in the database. This ensures that the message is persisted and can be retrieved or displayed at a later time.\n\n#### Finally, the function returns the message object, indicating the successful completion of the sending process.\n- You can see code below it's easy to understand and try to play around with them, Best way to understand working of any code is to make changes in them and undestand.", + "line": 206 + }, + { + "file": "apps/meteor/app/lib/client/methods/sendMessage.ts", + "description": "## The Async sendMessage Api call\n\n#### - As mentioned earlier, there are two Meteor methods for handling the sendMessage functionality and making API Call. The first method handles processing and validation of the message, while the second method addresses the delay in rendering the message on the frontend.\n\n#### - The second Meteor method is designed to ensure instant display of the sent message. When the first method completes successfully, it triggers the execution of the second method in an asynchronous manner. This allows for near-real-time rendering of the message on the user interface, reducing any noticeable delay.\n\n#### And in the end there is a similar callback as it was in previous step which helps in message rendering, The callback return IMessage object which is displayed to user\n\n#### - By separating these two methods, the application can provide a smooth and responsive user experience. The initial processing and validation are performed without blocking the UI, and once that is completed, the message is promptly displayed to the user.\n\n### You Have now successfully sent Message from composer, Note that there is one more API to sendMessage Which is an REST API, Which is currently not being used, if it would be used in future this tour would be updated. Still you can manually sendMessage by making call here [RestAPI Docs](https://developer.rocket.chat/reference/api/rest-api/endpoints/chat-endpoints/send-message) using -curl command, you can go thorugh the DOCS and try sending message on your own", + "line": 14 + } + ] +} \ No newline at end of file diff --git a/.tours/3---how-to-create-an-endpoint.tour b/.tours/4---how-to-create-an-endpoint.tour similarity index 88% rename from .tours/3---how-to-create-an-endpoint.tour rename to .tours/4---how-to-create-an-endpoint.tour index 8ad3ecb1b4d5..f6bfe6611fca 100644 --- a/.tours/3---how-to-create-an-endpoint.tour +++ b/.tours/4---how-to-create-an-endpoint.tour @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/codetour-schema", - "title": "3 - How to Create an Endpoint", + "title": "4 - How to Create an Endpoint", "steps": [ { "file": "apps/meteor/app/api/server/index.ts", @@ -39,8 +39,8 @@ }, { "file": "apps/meteor/app/api/server/v1/chat.ts", - "description": "## Calling REST endpoint\n\n### Let us try to call an REST Endpoint for sending message\n- In order to make Api call you need Personal Access Tokens for user authentication. **[Get Token here](https://docs.rocket.chat/use-rocket.chat/user-guides/user-panel/my-account#personal-access-tokens)** (Remember get access token from your own local server)\n\n- To get User-Id go to admin settings then users(or http://localhost:3000/admin/users) select your account and copy unique user id from URL (/admin/users/info/*u3y2jXw5ayckPciE9*) here we have **u3y2jXw5ayckPciE9** as user id\n\n- After you get you Personal access token Start Rocket Chat server on your local machine and in another terminal enter this command.\n\n```\ncurl -H \"X-Auth-Token: ENTER_YOUR_TOKEN \" \\\n -H \"X-User-Id: ENTER_YOUR_USERID \" \\\n -H \"Content-type:application/json\" \\\n http://localhost:3000/api/v1/chat.sendMessage \\\n -d '{\"message\":{\"rid\":\"GENERAL\", \"msg\":\"Hello From Rocket Chat\"}}'\n```\n- You will get a success response once your request is successful and vice versa\n\n### Similarly you can also create your own REST Endpoint with unique route name and define what it needs to do by adding different funtionalities in it. ", - "line": 227, + "description": "## Calling REST endpoint\n\n### Let us try to call an REST Endpoint for sending message\n- In order to make Api call you need Personal Access Tokens for user authentication. **[Get Token here](https://docs.rocket.chat/use-rocket.chat/user-guides/user-panel/my-account#personal-access-tokens)** (Remember get access token from your own local server)\n\n- To get User-Id go to admin settings then users(or http://localhost:3000/admin/users) select your account and copy unique user id from URL (http://localhost:3000/admin/users/info/u3y2jXw5ayckPciE9) here we have **u3y2jXw5ayckPciE9** as user id, Try finding yours\n\n- After you get you Personal access token Start Rocket Chat server on your local machine and in another terminal enter this command.\n\n```\ncurl -H \"X-Auth-Token: ENTER_YOUR_TOKEN \" \\\n -H \"X-User-Id: ENTER_YOUR_USERID \" \\\n -H \"Content-type:application/json\" \\\n http://localhost:3000/api/v1/chat.sendMessage \\\n -d '{\"message\":{\"rid\":\"GENERAL\", \"msg\":\"Hello From Rocket Chat\"}}'\n```\n- You will get a success response once your request is successful and vice versa\n\n### Similarly you can also create your own REST Endpoint with unique route name and define what it needs to do by adding different funtionalities in it. ", + "line": 239, "selection": { "start": { "line": 6, @@ -73,9 +73,9 @@ "line": 32 }, { - "file": "apps/meteor/client/lib/utils/call.ts", + "file": "apps/meteor/app/utils/client/lib/SDKClient.ts", "description": "## call method\n\n### With help of call method this method gets triggered and it takes method name and other paramerters for example here we passed **'sendMessage'** as method and **message** as parameters.\n\n### Then method is matched with existing endpoints, if method is found, for example sendMessage is found then further function will be executed. else we will get error as sendMessage does not exists", - "line": 4 + "line": 160 } ] } \ No newline at end of file diff --git a/.tours/5---how-to-create-a-db-model.tour b/.tours/5---how-to-create-a-db-model.tour new file mode 100644 index 000000000000..d52f46518177 --- /dev/null +++ b/.tours/5---how-to-create-a-db-model.tour @@ -0,0 +1,35 @@ +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "5 - How to create a DB model", + "steps": [ + { + "directory": "apps/meteor/server/models", + "description": "## How to create a DB Model \n\n### Rocket.Chat server relies on MongoDB as its primary database to store crucial information such as chat messages, user data, system configurations, and other related data. MongoDB plays a vital role in maintaining and organizing the essential information that powers Rocket.Chat.\n\n### Files like assets, user files, images, and other media files are stored locally on the system running the server or over network services like Amazon S3, and WebDAV.\n\n### Let us explore and see how can anyone create a new Database model, Here I will be giving example of Messages model and how things inside it works." + }, + { + "file": "apps/meteor/server/models/raw/Messages.ts", + "description": "## Messages Model\n\n- **Firstly we start by importing some important components and modules such as BaseRaw which contains Operations related to Database model.**\n\n- **We have *Rooms* model imported from *@rocket.chat/models* which is again a model like we are looking at right now, Checkout [Rooms](./apps/meteor/server/models/raw/Rooms.ts) Model**\n\n- **We also have imports from model-typings such as FindPaginated and IMessageModel which have type definitions for the model**\n\n- **We have multiple imports from mongodb as *AggregationCursor, Collection, FindCursor, UpdateResult, etc.* which help in mongodb operations**", + "line": 31 + }, + { + "file": "apps/meteor/server/models/raw/Messages.ts", + "description": "## MessageRaw class\n\n- **To register a DB model in Rocket.Chat, it is necessary to create a corresponding class for that model. In our case, as we are creating the Messages Model, we need to define a class specifically for it. This class will serve as the blueprint for the Messages Model and will be used for registering and interacting with the corresponding data in the database.**\n\n- **In order to facilitate the management of the Messages Model in Rocket.Chat's database, we have a class called MessageRaw. This class extends the BaseRaw class and implements the IMessageModel interface, which we discussed earlier. By extending BaseRaw and implementing the IMessageModel interface, MessageRaw inherits necessary functionalities and ensures it adheres to the required structure and behavior of the Messages Model in Rocket.Chat.**", + "line": 42 + }, + { + "file": "apps/meteor/server/models/raw/Messages.ts", + "description": "## Methods and Operations\n\n### From here onwards there are multiple methods and operations related with Message Model you can go through each of them and try to undestand what are they doing, It is easy to understand them.\n\n- **There are multiple methods such as**\n - ***findStarredByUserAtRoom***\n - ***findLivechatMessages***\n - ***findStarred***\n - ***setMessageAttachments***\n - ***getMessageByFileIdAndUsername***\n - and many more", + "line": 82 + }, + { + "file": "apps/meteor/server/models/Messages.ts", + "description": "## Registering a DB model\n\n### Now let us see how can we register any DB model\n\n### 1 - First of all we need to import *registeModel* from *@rocket.chat/models*\n\n### 2 - Import MessagesRaw- The DB model Class we created which includes operations, and we are also importing db, trashCollection- It contains deleted messages", + "line": 1 + }, + { + "file": "apps/meteor/server/models/Messages.ts", + "description": "## Registering\n\n### Here we are using the registerModel import and passing 'IMessageModel' and using class MessagesRaw we pass in db and trashCollection\n\n### The register model funtion looks something like this -\n```\nfunction registerModel>(name: string, instance: TModel | (() => TModel)): void;\n```\n\n### And we pass data into it like - \n```\n registerModel('IMessagesModel', new MessagesRaw(db, trashCollection));\n //It becomes something like this, Here IMessageModel is basically implemented in MessagesRaw as we saw in previous steps\n registerModel(name: string, instance: MessagesRaw | (() => MessagesRaw)): void\n```", + "line": 7 + } + ] +} \ No newline at end of file From c0fa567246209cc0b714c3dad67b28c6d14d43b8 Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto Date: Sat, 24 Jun 2023 16:40:41 -0300 Subject: [PATCH 009/149] feat(fuselage-ui-kit): Add `i18n` parser to render surfaces and blocks (#29413) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .changeset/rude-insects-speak.md | 6 ++++ apps/meteor/ee/client/apps/i18n.js | 6 +++- .../src/elements/MarkdownTextElement.tsx | 30 +++++++++++++++++++ .../src/elements/PlainTextElement.tsx | 25 ++++++++++++++++ .../src/surfaces/FuselageSurfaceRenderer.tsx | 21 ++++++------- yarn.lock | 6 ++-- 6 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 .changeset/rude-insects-speak.md create mode 100644 packages/fuselage-ui-kit/src/elements/MarkdownTextElement.tsx create mode 100644 packages/fuselage-ui-kit/src/elements/PlainTextElement.tsx diff --git a/.changeset/rude-insects-speak.md b/.changeset/rude-insects-speak.md new file mode 100644 index 000000000000..13b06ab68957 --- /dev/null +++ b/.changeset/rude-insects-speak.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/fuselage-ui-kit': minor +'@rocket.chat/meteor': minor +--- + +Introducing i18n to UiKit text renderers diff --git a/apps/meteor/ee/client/apps/i18n.js b/apps/meteor/ee/client/apps/i18n.js index c383ed804110..9b2c6ffe2cb7 100644 --- a/apps/meteor/ee/client/apps/i18n.js +++ b/apps/meteor/ee/client/apps/i18n.js @@ -6,13 +6,17 @@ import { Apps } from './orchestrator'; const loadAppI18nResources = (appId, languages) => { Object.entries(languages).forEach(([language, translations]) => { try { + const regex = /([a-z]{2,3})-([a-z]{2,4})/; + const match = regex.exec(language); + const normalizedLanguage = match ? `${match[1]}-${match[2].toUpperCase()}` : language; + // Translations keys must be scoped under app id const scopedTranslations = Object.entries(translations).reduce((translations, [key, value]) => { translations[Utilities.getI18nKeyForApp(key, appId)] = value; return translations; }, {}); - i18n.addResourceBundle(language, 'core', scopedTranslations); + i18n.addResourceBundle(normalizedLanguage, 'core', scopedTranslations); } catch (error) { Apps.handleError(error); } diff --git a/packages/fuselage-ui-kit/src/elements/MarkdownTextElement.tsx b/packages/fuselage-ui-kit/src/elements/MarkdownTextElement.tsx new file mode 100644 index 000000000000..b4d516bc8c77 --- /dev/null +++ b/packages/fuselage-ui-kit/src/elements/MarkdownTextElement.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from '@rocket.chat/ui-contexts'; +import { parse } from '@rocket.chat/message-parser'; +import { Markup } from '@rocket.chat/gazzodown'; +import type { TextObject } from '@rocket.chat/ui-kit'; + +import { useUiKitContext } from '../contexts/kitContext'; + +const MarkdownTextElement = ({ textObject }: { textObject: TextObject }) => { + const t = useTranslation() as ( + key: string, + args: { [key: string]: string | number } + ) => string; + const { appId } = useUiKitContext(); + + const { i18n } = textObject; + + if (i18n) { + return ( + + ); + } + + return ; +}; + +export default MarkdownTextElement; diff --git a/packages/fuselage-ui-kit/src/elements/PlainTextElement.tsx b/packages/fuselage-ui-kit/src/elements/PlainTextElement.tsx new file mode 100644 index 000000000000..3178caedd263 --- /dev/null +++ b/packages/fuselage-ui-kit/src/elements/PlainTextElement.tsx @@ -0,0 +1,25 @@ +import type { TextObject } from '@rocket.chat/ui-kit'; +import { Fragment } from 'react'; +import { useTranslation } from '@rocket.chat/ui-contexts'; + +import { useUiKitContext } from '../contexts/kitContext'; + +const PlainTextElement = ({ textObject }: { textObject: TextObject }) => { + const t = useTranslation() as ( + key: string, + args: { [key: string]: string | number } + ) => string; + const { appId } = useUiKitContext(); + + const { i18n } = textObject; + + if (i18n) { + return ( + {t(`apps-${appId}-${i18n.key}`, { ...i18n.args })} + ); + } + + return {textObject.text}; +}; + +export default PlainTextElement; diff --git a/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx b/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx index b2f4d1f9764f..c808878a9fb1 100644 --- a/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx +++ b/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx @@ -1,8 +1,5 @@ import * as UiKit from '@rocket.chat/ui-kit'; -import { parse } from '@rocket.chat/message-parser'; import type { ReactElement } from 'react'; -import { Fragment } from 'react'; -import { Markup } from '@rocket.chat/gazzodown'; import ActionsBlock from '../blocks/ActionsBlock'; import ContextBlock from '../blocks/ContextBlock'; @@ -19,6 +16,8 @@ import MultiStaticSelectElement from '../elements/MultiStaticSelectElement'; import OverflowElement from '../elements/OverflowElement'; import PlainTextInputElement from '../elements/PlainTextInputElement'; import StaticSelectElement from '../elements/StaticSelectElement'; +import MarkdownTextElement from '../elements/MarkdownTextElement'; +import PlainTextElement from '../elements/PlainTextElement'; export type FuselageSurfaceRendererProps = ConstructorParameters< typeof UiKit.SurfaceRenderer @@ -40,7 +39,7 @@ export class FuselageSurfaceRenderer extends UiKit.SurfaceRenderer } public plain_text( - { text = '' }: UiKit.PlainText, + textObject: UiKit.TextObject, context: UiKit.BlockContext, index: number ): ReactElement | null { @@ -48,11 +47,11 @@ export class FuselageSurfaceRenderer extends UiKit.SurfaceRenderer return null; } - return text ? {text} : null; + return ; } public mrkdwn( - { text = '' }: UiKit.Markdown, + textObject: UiKit.TextObject, context: UiKit.BlockContext, index: number ): ReactElement | null { @@ -60,9 +59,7 @@ export class FuselageSurfaceRenderer extends UiKit.SurfaceRenderer return null; } - return text ? ( - - ) : null; + return ; } public text( @@ -70,11 +67,11 @@ export class FuselageSurfaceRenderer extends UiKit.SurfaceRenderer context: UiKit.BlockContext, index: number ): ReactElement | null { - if (textObject.type !== 'mrkdwn') { - return this.plain_text(textObject, context, index); + if (textObject.type === 'mrkdwn') { + return this.mrkdwn(textObject, context, index); } - return this.mrkdwn(textObject, context, index); + return this.plain_text(textObject, context, index); } actions( diff --git a/yarn.lock b/yarn.lock index ce4fd467ba94..0b573475ee3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11008,9 +11008,9 @@ __metadata: linkType: soft "@rocket.chat/ui-kit@npm:next": - version: 0.32.0-dev.264 - resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.264" - checksum: f856ac22a422c862b84f5a550ca1358c313f0f42d74c1780eeb781394a9aca27a12572873c6d69edea92fdd092dcd4dd34dcfc8e45ecc91a34feec4220a80905 + version: 0.32.0-dev.294 + resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.294" + checksum: cf58890f3fbcfdff3df9436a33c4aa333de34369f89902107dee74c50c11b8af793c9c80a8df5046ffcabf0fd90111f31a6fc2030876e838e43ec4c6a5703fc4 languageName: node linkType: hard From 2ddb56618a4939d6e12a177b90f918b874e82d86 Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto Date: Mon, 26 Jun 2023 12:13:21 -0300 Subject: [PATCH 010/149] refactor: `AppsProvider` (#29264) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .../marketplace => contexts}/AppsContext.tsx | 17 +++------ .../client/contexts/hooks/useAppsReload.ts | 8 +++++ .../client/contexts/hooks/useAppsResult.ts | 6 ++++ .../AppsProvider.tsx | 30 +++++++--------- .../AppDetailsPage/AppDetailsPage.tsx | 4 +-- .../tabs/AppRequests/AppRequests.tsx | 2 +- .../views/marketplace/AppInstallPage.js | 6 ++-- .../marketplace/AppsPage/AppsPageContent.tsx | 28 +++++++-------- .../AppsPage/AppsPageContentBody.tsx | 13 +++---- .../client/views/marketplace/AppsRoute.tsx | 2 +- .../views/marketplace/helpers/installApp.ts | 4 +-- .../views/marketplace/helpers/updateApp.ts | 4 +-- .../views/marketplace/hooks/useAppInfo.ts | 8 ++--- .../hooks/useAppInstallationHandler.tsx | 6 ++-- .../views/marketplace/hooks/useCategories.ts | 4 +-- .../marketplace/hooks/useFilteredApps.ts | 2 +- .../hooks/useOpenIncompatibleModal.tsx | 8 +++-- apps/meteor/ee/client/apps/i18n.js | 12 +++---- apps/meteor/ee/client/apps/index.ts | 2 +- apps/meteor/ee/client/apps/orchestrator.ts | 35 +++++++------------ .../fragments/home-flextab-members.ts | 2 +- .../fragments/home-flextab-members.ts | 2 +- 22 files changed, 99 insertions(+), 106 deletions(-) rename apps/meteor/client/{views/marketplace => contexts}/AppsContext.tsx (57%) create mode 100644 apps/meteor/client/contexts/hooks/useAppsReload.ts create mode 100644 apps/meteor/client/contexts/hooks/useAppsResult.ts rename apps/meteor/client/{views/marketplace => providers}/AppsProvider.tsx (83%) diff --git a/apps/meteor/client/views/marketplace/AppsContext.tsx b/apps/meteor/client/contexts/AppsContext.tsx similarity index 57% rename from apps/meteor/client/views/marketplace/AppsContext.tsx rename to apps/meteor/client/contexts/AppsContext.tsx index d816d32f97de..769609c733b0 100644 --- a/apps/meteor/client/views/marketplace/AppsContext.tsx +++ b/apps/meteor/client/contexts/AppsContext.tsx @@ -1,10 +1,10 @@ -import { createContext, useContext } from 'react'; +import { createContext } from 'react'; -import type { AsyncState } from '../../lib/asyncState'; -import { AsyncStatePhase } from '../../lib/asyncState'; -import type { App } from './types'; +import type { AsyncState } from '../lib/asyncState'; +import { AsyncStatePhase } from '../lib/asyncState'; +import type { App } from '../views/marketplace/types'; -type AppsContextValue = { +export type AppsContextValue = { installedApps: AsyncState<{ apps: App[] }>; marketplaceApps: AsyncState<{ apps: App[] }>; privateApps: AsyncState<{ apps: App[] }>; @@ -29,10 +29,3 @@ export const AppsContext = createContext({ }, reload: () => Promise.resolve(), }); - -export const useAppsReload = (): (() => void) => { - const { reload } = useContext(AppsContext); - return reload; -}; - -export const useAppsResult = (): AppsContextValue => useContext(AppsContext); diff --git a/apps/meteor/client/contexts/hooks/useAppsReload.ts b/apps/meteor/client/contexts/hooks/useAppsReload.ts new file mode 100644 index 000000000000..276c30fe5a15 --- /dev/null +++ b/apps/meteor/client/contexts/hooks/useAppsReload.ts @@ -0,0 +1,8 @@ +import { useContext } from 'react'; + +import { AppsContext } from '../AppsContext'; + +export const useAppsReload = (): (() => void) => { + const { reload } = useContext(AppsContext); + return reload; +}; diff --git a/apps/meteor/client/contexts/hooks/useAppsResult.ts b/apps/meteor/client/contexts/hooks/useAppsResult.ts new file mode 100644 index 000000000000..fb8deed2a9cb --- /dev/null +++ b/apps/meteor/client/contexts/hooks/useAppsResult.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react'; + +import type { AppsContextValue } from '../AppsContext'; +import { AppsContext } from '../AppsContext'; + +export const useAppsResult = (): AppsContextValue => useContext(AppsContext); diff --git a/apps/meteor/client/views/marketplace/AppsProvider.tsx b/apps/meteor/client/providers/AppsProvider.tsx similarity index 83% rename from apps/meteor/client/views/marketplace/AppsProvider.tsx rename to apps/meteor/client/providers/AppsProvider.tsx index 0f1868ba375c..2b9e2c82cd74 100644 --- a/apps/meteor/client/views/marketplace/AppsProvider.tsx +++ b/apps/meteor/client/providers/AppsProvider.tsx @@ -3,13 +3,13 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; import type { FC } from 'react'; import React, { useEffect } from 'react'; -import { AppEvents } from '../../../ee/client/apps/communication'; -import { Apps } from '../../../ee/client/apps/orchestrator'; -import PageSkeleton from '../../components/PageSkeleton'; -import { AsyncStatePhase } from '../../lib/asyncState'; -import { AppsContext } from './AppsContext'; -import { useInvalidateAppsCountQueryCallback } from './hooks/useAppsCountQuery'; -import type { App } from './types'; +import { AppEvents } from '../../ee/client/apps/communication'; +import { AppClientOrchestratorInstance } from '../../ee/client/apps/orchestrator'; +import PageSkeleton from '../components/PageSkeleton'; +import { AppsContext } from '../contexts/AppsContext'; +import { AsyncStatePhase } from '../lib/asyncState'; +import { useInvalidateAppsCountQueryCallback } from '../views/marketplace/hooks/useAppsCountQuery'; +import type { App } from '../views/marketplace/types'; type ListenersMapping = { readonly [P in keyof typeof AppEvents]?: (...args: any[]) => void; @@ -23,11 +23,11 @@ const registerListeners = (listeners: ListenersMapping): (() => void) => { undefined >[]; for (const [event, callback] of entries) { - Apps.getWsListener()?.registerListener(AppEvents[event], callback); + AppClientOrchestratorInstance.getWsListener()?.registerListener(AppEvents[event], callback); } return (): void => { for (const [event, callback] of entries) { - Apps.getWsListener()?.unregisterListener(AppEvents[event], callback); + AppClientOrchestratorInstance.getWsListener()?.unregisterListener(AppEvents[event], callback); } }; }; @@ -68,7 +68,7 @@ const AppsProvider: FC = ({ children }) => { const marketplace = useQuery( ['marketplace', 'apps-marketplace', isAdminUser], () => { - const result = Apps.getAppsFromMarketplace(isAdminUser ? 'true' : 'false'); + const result = AppClientOrchestratorInstance.getAppsFromMarketplace(isAdminUser); queryClient.invalidateQueries(['marketplace', 'apps-stored']); return result; }, @@ -83,7 +83,7 @@ const AppsProvider: FC = ({ children }) => { const instance = useQuery( ['marketplace', 'apps-instance', isAdminUser], async () => { - const result = await Apps.getInstalledApps().then((result: App[]) => + const result = await AppClientOrchestratorInstance.getInstalledApps().then((result: App[]) => result.map((current: App) => ({ ...current, installed: true, @@ -109,7 +109,6 @@ const AppsProvider: FC = ({ children }) => { const marketplaceApps: App[] = []; const installedApps: App[] = []; const privateApps: App[] = []; - const clonedData = [...instance.data]; sortByName(marketplace.data).forEach((app) => { @@ -130,13 +129,10 @@ const AppsProvider: FC = ({ children }) => { marketplaceVersion: app.version, }; - if (installedApp?.private) { - privateApps.push(record); - } - - if (installedApp && !installedApp.private) { + if (installedApp) { installedApps.push(record); } + marketplaceApps.push(record); }); diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx index 83374c8f981a..55d01c63a0c5 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx @@ -14,7 +14,7 @@ import type { ReactElement } from 'react'; import React, { useState, useCallback, useRef } from 'react'; import type { ISettings } from '../../../../ee/client/apps/@types/IOrchestrator'; -import { Apps } from '../../../../ee/client/apps/orchestrator'; +import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; import Page from '../../../components/Page'; import { handleAPIError } from '../helpers/handleAPIError'; import { useAppInfo } from '../hooks/useAppInfo'; @@ -61,7 +61,7 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { const { current } = settingsRef; setIsSaving(true); try { - await Apps.setAppSettings( + await AppClientOrchestratorInstance.setAppSettings( id, (Object.values(settings || {}) as ISetting[]).map((value) => ({ ...value, diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx index 1698d60290c4..228c625cf2df 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppRequests/AppRequests.tsx @@ -5,8 +5,8 @@ import { useMutation } from '@tanstack/react-query'; import type { ReactElement, SetStateAction } from 'react'; import React, { useState, useEffect } from 'react'; +import { useAppsReload } from '../../../../../contexts/hooks/useAppsReload'; import { queryClient } from '../../../../../lib/queryClient'; -import { useAppsReload } from '../../../AppsContext'; import { useAppRequests } from '../../../hooks/useAppRequests'; import AppRequestItem from './AppRequestItem'; import AppRequestsLoading from './AppRequestsLoading'; diff --git a/apps/meteor/client/views/marketplace/AppInstallPage.js b/apps/meteor/client/views/marketplace/AppInstallPage.js index 896be9061912..41c64ca0479c 100644 --- a/apps/meteor/client/views/marketplace/AppInstallPage.js +++ b/apps/meteor/client/views/marketplace/AppInstallPage.js @@ -11,13 +11,13 @@ import { } from '@rocket.chat/ui-contexts'; import React, { useCallback, useEffect, useState } from 'react'; -import { Apps } from '../../../ee/client/apps/orchestrator'; +import { AppClientOrchestratorInstance } from '../../../ee/client/apps/orchestrator'; import Page from '../../components/Page'; +import { useAppsReload } from '../../contexts/hooks/useAppsReload'; import { useFileInput } from '../../hooks/useFileInput'; import { useForm } from '../../hooks/useForm'; import AppPermissionsReviewModal from './AppPermissionsReviewModal'; import AppUpdateModal from './AppUpdateModal'; -import { useAppsReload } from './AppsContext'; import AppInstallModal from './components/AppInstallModal/AppInstallModal'; import { handleAPIError, handleInstallError } from './helpers'; import { useAppsCountQuery } from './hooks/useAppsCountQuery'; @@ -102,7 +102,7 @@ function AppInstallPage() { const isAppInstalled = async (appId) => { try { - const app = await Apps.getApp(appId); + const app = await AppClientOrchestratorInstance.getApp(appId); return !!app || false; } catch (e) { return false; diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx index f0173ea20450..1e2c79fe40f1 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx @@ -4,8 +4,8 @@ import type { ReactElement } from 'react'; import React, { useEffect, useMemo, useState, useCallback } from 'react'; import { usePagination } from '../../../components/GenericTable/hooks/usePagination'; +import { useAppsResult } from '../../../contexts/hooks/useAppsResult'; import { AsyncStatePhase } from '../../../lib/asyncState'; -import { useAppsReload, useAppsResult } from '../AppsContext'; import type { RadioDropDownGroup } from '../definitions/RadioDropDownDefinitions'; import { useCategories } from '../hooks/useCategories'; import type { appsDataType } from '../hooks/useFilteredApps'; @@ -23,9 +23,8 @@ import PrivateEmptyState from './PrivateEmptyState'; const AppsPageContent = (): ReactElement => { const t = useTranslation(); - const { marketplaceApps, installedApps, privateApps } = useAppsResult(); + const { marketplaceApps, installedApps, privateApps, reload } = useAppsResult(); const [text, setText] = useDebouncedState('', 500); - const reload = useAppsReload(); const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination(); const [currentRouteName] = useCurrentRoute(); @@ -36,10 +35,8 @@ const AppsPageContent = (): ReactElement => { const context = useRouteParameter('context'); - const isEnterprise = context === 'enterprise'; const isMarketplace = context === 'explore'; const isRequested = context === 'requested'; - const isPrivate = context === 'private'; const [freePaidFilterStructure, setFreePaidFilterStructure] = useState({ label: t('Filter_By_Price'), @@ -76,16 +73,17 @@ const AppsPageContent = (): ReactElement => { const sortFilterOnSelected = useRadioToggle(setSortFilterStructure); const getAppsData = useCallback((): appsDataType => { - if (isMarketplace || isEnterprise || isRequested) { - return marketplaceApps; + switch (context) { + case 'enterprise': + case 'explore': + case 'requested': + return marketplaceApps; + case 'private': + return privateApps; + default: + return installedApps; } - - if (isPrivate) { - return privateApps; - } - - return installedApps; - }, [isMarketplace, isEnterprise, isRequested, isPrivate, installedApps, marketplaceApps, privateApps]); + }, [context, marketplaceApps, installedApps, privateApps]); const [categories, selectedCategories, categoryTagList, onSelected] = useCategories(); const appsResult = useFilteredApps({ @@ -172,7 +170,7 @@ const AppsPageContent = (): ReactElement => { ; + appsResult: { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }; itemsPerPage: 25 | 50 | 100; current: number; onSetItemsPerPage: React.Dispatch>; @@ -44,17 +41,17 @@ const AppsPageContentBody = ({ {noErrorsOcurred && ( - {isMarketplace && !isFiltered && } - + {isMarketplace && !isFiltered && } + )} - {Boolean(appsResult?.value?.count) && ( + {Boolean(appsResult?.count) && ( { onSetCurrent(value); diff --git a/apps/meteor/client/views/marketplace/AppsRoute.tsx b/apps/meteor/client/views/marketplace/AppsRoute.tsx index f7e930a86d45..0b0ca342e0ad 100644 --- a/apps/meteor/client/views/marketplace/AppsRoute.tsx +++ b/apps/meteor/client/views/marketplace/AppsRoute.tsx @@ -3,11 +3,11 @@ import type { ReactElement } from 'react'; import React, { useState, useEffect } from 'react'; import PageSkeleton from '../../components/PageSkeleton'; +import AppsProvider from '../../providers/AppsProvider'; import NotAuthorizedPage from '../notAuthorized/NotAuthorizedPage'; import AppDetailsPage from './AppDetailsPage'; import AppInstallPage from './AppInstallPage'; import AppsPage from './AppsPage'; -import AppsProvider from './AppsProvider'; import BannerEnterpriseTrialEnded from './components/BannerEnterpriseTrialEnded'; const AppsRoute = (): ReactElement => { diff --git a/apps/meteor/client/views/marketplace/helpers/installApp.ts b/apps/meteor/client/views/marketplace/helpers/installApp.ts index 6d51c4813b18..ed909b571a21 100644 --- a/apps/meteor/client/views/marketplace/helpers/installApp.ts +++ b/apps/meteor/client/views/marketplace/helpers/installApp.ts @@ -1,6 +1,6 @@ import type { App, AppPermission } from '@rocket.chat/core-typings'; -import { Apps } from '../../../../ee/client/apps/orchestrator'; +import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; import { handleAPIError } from './handleAPIError'; import { warnAppInstall } from './warnAppInstall'; @@ -10,7 +10,7 @@ type installAppProps = App & { export const installApp = async ({ id, name, marketplaceVersion, permissionsGranted }: installAppProps): Promise => { try { - const { status } = await Apps.installApp(id, marketplaceVersion, permissionsGranted); + const { status } = await AppClientOrchestratorInstance.installApp(id, marketplaceVersion, permissionsGranted); if (status) { warnAppInstall(name, status); } diff --git a/apps/meteor/client/views/marketplace/helpers/updateApp.ts b/apps/meteor/client/views/marketplace/helpers/updateApp.ts index a7b2e31be296..fbcc3a2d5fa3 100644 --- a/apps/meteor/client/views/marketplace/helpers/updateApp.ts +++ b/apps/meteor/client/views/marketplace/helpers/updateApp.ts @@ -1,6 +1,6 @@ import type { App, AppPermission } from '@rocket.chat/core-typings'; -import { Apps } from '../../../../ee/client/apps/orchestrator'; +import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; import { handleAPIError } from './handleAPIError'; import { warnStatusChange } from './warnStatusChange'; @@ -10,7 +10,7 @@ type updateAppProps = App & { export const updateApp = async ({ id, name, marketplaceVersion, permissionsGranted }: updateAppProps): Promise => { try { - const { status } = await Apps.updateApp(id, marketplaceVersion, permissionsGranted); + const { status } = await AppClientOrchestratorInstance.updateApp(id, marketplaceVersion, permissionsGranted); if (status) { warnStatusChange(name, status); } diff --git a/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts b/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts index de95080cd4f8..7e69cdeef97c 100644 --- a/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts +++ b/apps/meteor/client/views/marketplace/hooks/useAppInfo.ts @@ -3,8 +3,8 @@ import { useEndpoint } from '@rocket.chat/ui-contexts'; import { useState, useEffect, useContext } from 'react'; import type { ISettings } from '../../../../ee/client/apps/@types/IOrchestrator'; -import { Apps } from '../../../../ee/client/apps/orchestrator'; -import { AppsContext } from '../AppsContext'; +import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; +import { AppsContext } from '../../../contexts/AppsContext'; import type { AppInfo } from '../definitions/AppInfo'; const getBundledInApp = async (app: App): Promise => { @@ -12,7 +12,7 @@ const getBundledInApp = async (app: App): Promise => { return Promise.all( bundledIn.map(async (bundle) => { - const apps = await Apps.getAppsOnBundle(bundle.bundleId); + const apps = await AppClientOrchestratorInstance.getAppsOnBundle(bundle.bundleId); bundle.apps = apps.slice(0, 4); return bundle; }), @@ -83,7 +83,7 @@ export const useAppInfo = (appId: string, context: string): AppInfo | undefined }; fetchAppInfo(); - }, [appId, context, getApis, getBundledIn, getScreenshots, getSettings, installedApps, marketplaceApps, privateApps.value]); + }, [appId, context, getApis, getBundledIn, getScreenshots, getSettings, installedApps, marketplaceApps, privateApps.value?.apps]); return appData; }; diff --git a/apps/meteor/client/views/marketplace/hooks/useAppInstallationHandler.tsx b/apps/meteor/client/views/marketplace/hooks/useAppInstallationHandler.tsx index af0b04469cd5..666ea7bf7ae0 100644 --- a/apps/meteor/client/views/marketplace/hooks/useAppInstallationHandler.tsx +++ b/apps/meteor/client/views/marketplace/hooks/useAppInstallationHandler.tsx @@ -2,7 +2,7 @@ import type { App } from '@rocket.chat/core-typings'; import { useEndpoint, useRoute, useRouteParameter, useSetModal, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; -import { Apps } from '../../../../ee/client/apps/orchestrator'; +import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; import IframeModal from '../IframeModal'; import AppInstallModal from '../components/AppInstallModal/AppInstallModal'; import type { Actions } from '../helpers'; @@ -51,7 +51,7 @@ export function useAppInstallationHandler({ app, action, isAppPurchased, onDismi const acquireApp = useCallback(async () => { if (action === 'purchase' && !isAppPurchased) { try { - const data = await Apps.buildExternalUrl(app.id, app.purchaseType, false); + const data = await AppClientOrchestratorInstance.buildExternalUrl(app.id, app.purchaseType, false); setModal(); } catch (error) { handleAPIError(error); @@ -84,7 +84,7 @@ export function useAppInstallationHandler({ app, action, isAppPurchased, onDismi }; try { - const data = await Apps.buildExternalAppRequest(app.id); + const data = await AppClientOrchestratorInstance.buildExternalAppRequest(app.id); setModal(); } catch (error) { handleAPIError(error); diff --git a/apps/meteor/client/views/marketplace/hooks/useCategories.ts b/apps/meteor/client/views/marketplace/hooks/useCategories.ts index 27f01f91e04b..4a457a146046 100644 --- a/apps/meteor/client/views/marketplace/hooks/useCategories.ts +++ b/apps/meteor/client/views/marketplace/hooks/useCategories.ts @@ -1,7 +1,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { Apps } from '../../../../ee/client/apps/orchestrator'; +import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; import type { CategoryDropDownGroups, CategoryDropdownItem, @@ -19,7 +19,7 @@ export const useCategories = (): [CategoryDropDownGroups, selectedCategoriesList const fetchCategories = useCallback(async (): Promise => { try { - const fetchedCategories = await Apps.getCategories(); + const fetchedCategories = await AppClientOrchestratorInstance.getCategories(); const mappedCategories = fetchedCategories.map((currentCategory) => ({ id: currentCategory.id, diff --git a/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts b/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts index 1d14c34c7be4..19664cd4b693 100644 --- a/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts +++ b/apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts @@ -2,9 +2,9 @@ import type { PaginatedResult } from '@rocket.chat/rest-typings'; import type { ContextType } from 'react'; import { useMemo } from 'react'; +import type { AppsContext } from '../../../contexts/AppsContext'; import type { AsyncState } from '../../../lib/asyncState'; import { AsyncStatePhase } from '../../../lib/asyncState'; -import type { AppsContext } from '../AppsContext'; import { filterAppsByCategories } from '../helpers/filterAppsByCategories'; import { filterAppsByDisabled } from '../helpers/filterAppsByDisabled'; import { filterAppsByEnabled } from '../helpers/filterAppsByEnabled'; diff --git a/apps/meteor/client/views/marketplace/hooks/useOpenIncompatibleModal.tsx b/apps/meteor/client/views/marketplace/hooks/useOpenIncompatibleModal.tsx index 41bf7716b1eb..b80bdb9a61a7 100644 --- a/apps/meteor/client/views/marketplace/hooks/useOpenIncompatibleModal.tsx +++ b/apps/meteor/client/views/marketplace/hooks/useOpenIncompatibleModal.tsx @@ -1,7 +1,7 @@ import { useSetModal } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; -import { Apps } from '../../../../ee/client/apps/orchestrator'; +import { AppClientOrchestratorInstance } from '../../../../ee/client/apps/orchestrator'; import IframeModal from '../IframeModal'; import { handleAPIError } from '../helpers/handleAPIError'; @@ -21,7 +21,11 @@ export const useOpenIncompatibleModal = () => { }; try { - const incompatibleData = await Apps.buildIncompatibleExternalUrl(app.id, app.marketplaceVersion, actionName); + const incompatibleData = await AppClientOrchestratorInstance.buildIncompatibleExternalUrl( + app.id, + app.marketplaceVersion, + actionName, + ); setModal(); } catch (e) { handleAPIError(e); diff --git a/apps/meteor/ee/client/apps/i18n.js b/apps/meteor/ee/client/apps/i18n.js index 9b2c6ffe2cb7..bdece5e69061 100644 --- a/apps/meteor/ee/client/apps/i18n.js +++ b/apps/meteor/ee/client/apps/i18n.js @@ -1,7 +1,7 @@ import { i18n } from '../../../app/utils/lib/i18n'; import { Utilities } from '../../lib/misc/Utilities'; import { AppEvents } from './communication'; -import { Apps } from './orchestrator'; +import { AppClientOrchestratorInstance } from './orchestrator'; const loadAppI18nResources = (appId, languages) => { Object.entries(languages).forEach(([language, translations]) => { @@ -18,22 +18,22 @@ const loadAppI18nResources = (appId, languages) => { i18n.addResourceBundle(normalizedLanguage, 'core', scopedTranslations); } catch (error) { - Apps.handleError(error); + AppClientOrchestratorInstance.handleError(error); } }); }; const handleAppAdded = async (appId) => { - const languages = await Apps.getAppLanguages(appId); + const languages = await AppClientOrchestratorInstance.getAppLanguages(appId); loadAppI18nResources(appId, languages); }; export const handleI18nResources = async () => { - const apps = await Apps.getAppsLanguages(); + const apps = await AppClientOrchestratorInstance.getAppsLanguages(); apps.forEach(({ id, languages }) => { loadAppI18nResources(id, languages); }); - Apps.getWsListener().unregisterListener(AppEvents.APP_ADDED, handleAppAdded); - Apps.getWsListener().registerListener(AppEvents.APP_ADDED, handleAppAdded); + AppClientOrchestratorInstance.getWsListener().unregisterListener(AppEvents.APP_ADDED, handleAppAdded); + AppClientOrchestratorInstance.getWsListener().registerListener(AppEvents.APP_ADDED, handleAppAdded); }; diff --git a/apps/meteor/ee/client/apps/index.ts b/apps/meteor/ee/client/apps/index.ts index 2e909abf81e5..2c81f48ecdd6 100644 --- a/apps/meteor/ee/client/apps/index.ts +++ b/apps/meteor/ee/client/apps/index.ts @@ -1,3 +1,3 @@ import './gameCenter/tabBar'; -export { Apps } from './orchestrator'; +export { AppClientOrchestratorInstance as Apps } from './orchestrator'; diff --git a/apps/meteor/ee/client/apps/orchestrator.ts b/apps/meteor/ee/client/apps/orchestrator.ts index c005222796d1..80a8e2cbf6ef 100644 --- a/apps/meteor/ee/client/apps/orchestrator.ts +++ b/apps/meteor/ee/client/apps/orchestrator.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ import { AppClientManager } from '@rocket.chat/apps-engine/client/AppClientManager'; import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { IApiEndpointMetadata } from '@rocket.chat/apps-engine/definition/api'; @@ -13,15 +12,7 @@ import { CachedCollectionManager } from '../../../app/ui-cached-collection/clien import { sdk } from '../../../app/utils/client/lib/SDKClient'; import { dispatchToastMessage } from '../../../client/lib/toast'; import type { App } from '../../../client/views/marketplace/types'; -import type { - // IAppFromMarketplace, - IAppLanguage, - IAppExternalURL, - ICategory, - // IAppSynced, - // IAppScreenshots, - // IScreenshot, -} from './@types/IOrchestrator'; +import type { IAppLanguage, IAppExternalURL, ICategory } from './@types/IOrchestrator'; import { RealAppsEngineUIHost } from './RealAppsEngineUIHost'; import { AppWebsocketReceiver } from './communication'; import { handleI18nResources } from './i18n'; @@ -31,27 +22,27 @@ class AppClientOrchestrator { private _manager: AppClientManager; - private isLoaded: boolean; + private _isLoaded: boolean; - private ws: AppWebsocketReceiver; + private _ws: AppWebsocketReceiver; constructor() { this._appClientUIHost = new RealAppsEngineUIHost(); this._manager = new AppClientManager(this._appClientUIHost); - this.isLoaded = false; + this._isLoaded = false; } public async load(): Promise { - if (!this.isLoaded) { - this.ws = new AppWebsocketReceiver(); - this.isLoaded = true; + if (!this._isLoaded) { + this._ws = new AppWebsocketReceiver(); + this._isLoaded = true; } await handleI18nResources(); } public getWsListener(): AppWebsocketReceiver { - return this.ws; + return this._ws; } public getAppClientManager(): AppClientManager { @@ -82,8 +73,8 @@ class AppClientOrchestrator { throw new Error('Invalid response from API'); } - public async getAppsFromMarketplace(isAdminUser?: string): Promise { - const result = await sdk.rest.get('/apps/marketplace', { isAdminUser }); + public async getAppsFromMarketplace(isAdminUser?: boolean): Promise { + const result = await sdk.rest.get('/apps/marketplace', { isAdminUser: isAdminUser ? isAdminUser.toString() : 'false' }); if (!Array.isArray(result)) { // TODO: chapter day: multiple results are returned, but we only need one @@ -271,11 +262,11 @@ class AppClientOrchestrator { } } -export const Apps = new AppClientOrchestrator(); +export const AppClientOrchestratorInstance = new AppClientOrchestrator(); Meteor.startup(() => { CachedCollectionManager.onLogin(() => { - Apps.getAppClientManager().initialize(); - Apps.load(); + AppClientOrchestratorInstance.getAppClientManager().initialize(); + AppClientOrchestratorInstance.load(); }); }); diff --git a/apps/meteor/tests/e2e/federation/page-objects/fragments/home-flextab-members.ts b/apps/meteor/tests/e2e/federation/page-objects/fragments/home-flextab-members.ts index a690c3559167..929fd0299aef 100644 --- a/apps/meteor/tests/e2e/federation/page-objects/fragments/home-flextab-members.ts +++ b/apps/meteor/tests/e2e/federation/page-objects/fragments/home-flextab-members.ts @@ -50,6 +50,6 @@ export class FederationHomeFlextabMembers { async showAllUsers() { await this.page.locator('.rcx-select >> text=Online').first().click(); - await this.page.locator('.rcx-option__content:has-text("All")').first().click(); + await this.page.locator('.rcx-option:has-text("All")').first().click(); } } diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-flextab-members.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab-members.ts index dc8b04455a6e..bbcd911a451f 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-flextab-members.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-flextab-members.ts @@ -39,6 +39,6 @@ export class HomeFlextabMembers { async showAllUsers() { await this.page.locator('.rcx-select >> text=Online').first().click(); - await this.page.locator('.rcx-option__content:has-text("All")').first().click(); + await this.page.locator('.rcx-option:has-text("All")').first().click(); } } From 3126b51a81cc9f4a1bbd23f184db435562324772 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Mon, 26 Jun 2023 15:55:58 -0300 Subject: [PATCH 011/149] fix: Canned responses filter not working (#29648) --- .../omnichannel/cannedResponses/CannedResponsesTable.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx index fa5b4eb67ec3..d179a14458b9 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx @@ -67,7 +67,10 @@ const CannedResponsesTable = () => { ); const getCannedResponses = useEndpoint('GET', '/v1/canned-responses'); - const { data, isLoading, isSuccess, refetch } = useQuery(['canned-responses', debouncedText], () => getCannedResponses(query)); + const { data, isLoading, isSuccess, refetch } = useQuery( + ['/v1/canned-responses', { debouncedText, sortDirection, itemsPerPage, current, sharing, createdBy }], + () => getCannedResponses(query), + ); const getTime = useFormatDateAndTime(); From 610f3e92e05b2d26950ccde04e484a115cdb71fc Mon Sep 17 00:00:00 2001 From: "lingohub[bot]" <69908207+lingohub[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 19:52:30 +0000 Subject: [PATCH 012/149] =?UTF-8?q?i18n:=20Language=20update=20from=20Ling?= =?UTF-8?q?oHub=20=F0=9F=A4=96=20on=202023-06-26Z=20(#29646)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .../rocketchat-i18n/i18n/af.i18n.json | 4 +- .../rocketchat-i18n/i18n/ar.i18n.json | 7 +- .../rocketchat-i18n/i18n/az.i18n.json | 4 +- .../rocketchat-i18n/i18n/be-BY.i18n.json | 4 +- .../rocketchat-i18n/i18n/bg.i18n.json | 4 +- .../rocketchat-i18n/i18n/bs.i18n.json | 4 +- .../rocketchat-i18n/i18n/ca.i18n.json | 7 +- .../rocketchat-i18n/i18n/cs.i18n.json | 5 +- .../rocketchat-i18n/i18n/cy.i18n.json | 4 +- .../rocketchat-i18n/i18n/da.i18n.json | 5 +- .../rocketchat-i18n/i18n/de-AT.i18n.json | 4 +- .../rocketchat-i18n/i18n/de.i18n.json | 30 +++++++-- .../rocketchat-i18n/i18n/el.i18n.json | 4 +- .../rocketchat-i18n/i18n/en.i18n.json | 2 +- .../rocketchat-i18n/i18n/eo.i18n.json | 4 +- .../rocketchat-i18n/i18n/es.i18n.json | 6 +- .../rocketchat-i18n/i18n/fa.i18n.json | 4 +- .../rocketchat-i18n/i18n/fi.i18n.json | 7 +- .../rocketchat-i18n/i18n/fr.i18n.json | 7 +- .../rocketchat-i18n/i18n/he.i18n.json | 4 +- .../rocketchat-i18n/i18n/hi-IN.i18n.json | 2 +- .../rocketchat-i18n/i18n/hi.i18n.json | 2 +- .../rocketchat-i18n/i18n/hr.i18n.json | 4 +- .../rocketchat-i18n/i18n/hu.i18n.json | 7 +- .../rocketchat-i18n/i18n/id.i18n.json | 4 +- .../rocketchat-i18n/i18n/it.i18n.json | 4 +- .../rocketchat-i18n/i18n/ja.i18n.json | 7 +- .../rocketchat-i18n/i18n/ka-GE.i18n.json | 4 +- .../rocketchat-i18n/i18n/km.i18n.json | 4 +- .../rocketchat-i18n/i18n/ko.i18n.json | 5 +- .../rocketchat-i18n/i18n/ku.i18n.json | 4 +- .../rocketchat-i18n/i18n/lo.i18n.json | 4 +- .../rocketchat-i18n/i18n/lt.i18n.json | 4 +- .../rocketchat-i18n/i18n/lv.i18n.json | 4 +- .../rocketchat-i18n/i18n/mn.i18n.json | 4 +- .../rocketchat-i18n/i18n/ms-MY.i18n.json | 4 +- .../rocketchat-i18n/i18n/nl.i18n.json | 7 +- .../rocketchat-i18n/i18n/no.i18n.json | 4 +- .../rocketchat-i18n/i18n/pl.i18n.json | 7 +- .../rocketchat-i18n/i18n/pt-BR.i18n.json | 7 +- .../rocketchat-i18n/i18n/pt.i18n.json | 4 +- .../rocketchat-i18n/i18n/ro.i18n.json | 4 +- .../rocketchat-i18n/i18n/ru.i18n.json | 66 +++++++++++++++---- .../rocketchat-i18n/i18n/sk-SK.i18n.json | 4 +- .../rocketchat-i18n/i18n/sl-SI.i18n.json | 4 +- .../rocketchat-i18n/i18n/sq.i18n.json | 4 +- .../rocketchat-i18n/i18n/sr.i18n.json | 4 +- .../rocketchat-i18n/i18n/sv.i18n.json | 7 +- .../rocketchat-i18n/i18n/ta-IN.i18n.json | 4 +- .../rocketchat-i18n/i18n/th-TH.i18n.json | 4 +- .../rocketchat-i18n/i18n/tr.i18n.json | 4 +- .../rocketchat-i18n/i18n/ug.i18n.json | 4 +- .../rocketchat-i18n/i18n/uk.i18n.json | 4 +- .../rocketchat-i18n/i18n/vi-VN.i18n.json | 4 +- .../rocketchat-i18n/i18n/zh-HK.i18n.json | 4 +- .../rocketchat-i18n/i18n/zh-TW.i18n.json | 7 +- .../rocketchat-i18n/i18n/zh.i18n.json | 5 +- 57 files changed, 199 insertions(+), 150 deletions(-) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json index 2efae96e645f..cc915ea21186 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Bevestig nuwe wagwoord", "Confirm_New_Password_Placeholder": "Voer asseblief nuwe wagwoord weer in ...", "Confirm_password": "Bevestig jou wagwoord", + "Confirm_your_password": "Bevestig jou wagwoord", "Connection_Closed": "Verbinding gesluit", "Connection_Reset": "Verbinding herstel", "Consulting": "Consulting", @@ -2474,7 +2475,6 @@ "Type_your_job_title": "Tik jou werk titel", "Type_your_message": "Tik jou boodskap", "Type_your_name": "Tik jou naam", - "Type_your_new_password": "Tik jou nuwe wagwoord", "Type_your_password": "Tik jou wagwoord", "Type_your_username": "Tik jou gebruikersnaam", "UI_Allow_room_names_with_special_chars": "Laat spesiale karakters in kamer name toe", @@ -2759,4 +2759,4 @@ "registration.component.form.invalidConfirmPass": "Die wagwoord bevestiging pas nie by die wagwoord nie", "registration.component.form.confirmPassword": "Bevestig jou wagwoord", "registration.component.form.sendConfirmationEmail": "Stuur bevestiging e-pos" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json index 99df7b5a6035..f2691bb674b5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json @@ -922,6 +922,7 @@ "Confirm_new_password": "تأكيد كلمة المرور الجديدة", "Confirm_New_Password_Placeholder": "jEv[n إعادة إدخال كلمة المرور الجديدة...", "Confirm_password": "تأكيد كلمة المرور", + "Confirm_your_password": "تأكيد كلمة السر", "Confirmation": "التأكيد", "Connect": "اتصال", "Connected": "تم الاتصال", @@ -2675,7 +2676,7 @@ "Livechat_OfflineMessageToChannel_enabled": "إرسال رسائل Livechat من دون اتصال إلى قناة", "Omnichannel_on_hold_chat_resumed": "تم استئناف الدردشة قيد الانتظار: {{comment}}", "Omnichannel_on_hold_chat_automatically": "تم استئناف الدردشة تلقائيًا من \"قيد الانتظار\" عند تلقي رسالة جديدة من {{guest}}", - "Omnichannel_on_hold_chat_manually": "تم استئناف الدردشة يدويًا من \"قيد الانتظار\" بواسطة {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "تم استئناف الدردشة يدويًا من \"قيد الانتظار\" بواسطة {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "تم وضع الدردشة تلقائيًا قيد الانتظار لأننا لم نتلق أي رد من {{guest}} في {{timeout}} من الثواني", "Omnichannel_On_Hold_manually": "تم وضع الدردشة يدويًا قيد الانتظار بواسطة {{user}}", "Omnichannel_onHold_Chat": "وضع الدردشة قيد الانتظار", @@ -3921,7 +3922,6 @@ "Show_Avatars": "إظهار الصور الرمزية", "Show_counter": "عرض العداد", "Show_email_field": "عرض حقل البريد الإلكتروني", - "Show_Message_In_Main_Thread": "عرض رسائل الموضوع في الموضوع الرئيسي", "Show_more": "عرض المزيد", "Show_name_field": "عرض حقل الاسم", "show_offline_users": "عرض المستخدمين غير المتصلين", @@ -4373,7 +4373,6 @@ "Type_your_job_title": "اكتب المسمى الوظيفي الخاص بك", "Type_your_message": "اكتب رسالتك", "Type_your_name": "اكتب اسمك", - "Type_your_new_password": "اكتب كلمة المرور الجديدة", "Type_your_password": "اكتب كلمة المرور الخاصة بك", "Type_your_username": "اكتب اسم المستخدم الخاص بك", "UI_Allow_room_names_with_special_chars": "السماح بالأحرف الخاصة في أسماء Room", @@ -4945,4 +4944,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "قناة متعددة الاتجاهات", "RegisterWorkspace_Setup_Label": "البريد الإلكتروني لحساب السحابة", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "أوافق على <1>البنود والشروط و<3>سياسة الخصوصية" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json index 0c39addf7498..a44c25a1b95b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Yeni Şifrəni təsdiq", "Confirm_New_Password_Placeholder": "Yeni parol yenidən daxil edin ...", "Confirm_password": "Şifrənizi təsdiqləyin", + "Confirm_your_password": "Şifrənizi təsdiqləyin", "Connection_Closed": "Bağlantı bağlanıb", "Connection_Reset": "Bağlantı sıfırlandı", "Consulting": "Məsləhətçilik", @@ -2474,7 +2475,6 @@ "Type_your_job_title": "İşinizin adını yazın", "Type_your_message": "Mesajınızı yazın", "Type_your_name": "Adınızı yazın", - "Type_your_new_password": "Yeni parolunuzu yazın", "Type_your_password": "Şifrənizi yazın", "Type_your_username": "İstifadəçi adınızı yazın", "UI_Allow_room_names_with_special_chars": "Otaq adlarında xüsusi simvollara icazə verin", @@ -2759,4 +2759,4 @@ "registration.component.form.invalidConfirmPass": "Şifrənin təsdiqlənməsi şifrə uyğun gəlmir", "registration.component.form.confirmPassword": "Şifrənizi təsdiqləyin", "registration.component.form.sendConfirmationEmail": "Təsdiq e-poçt göndər" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json index d6de507a89d3..e448574f6b48 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json @@ -508,6 +508,7 @@ "Confirm_new_password": "Пацвердзіце новы пароль", "Confirm_New_Password_Placeholder": "Калі ласка, паўторна ўвесці новы пароль ...", "Confirm_password": "Пацвердзіць пароль", + "Confirm_your_password": "Пацвердзіць пароль", "Connection_Closed": "Сувязь спынена", "Connection_Reset": "скід падлучэння", "Consulting": "кансалтынг", @@ -2492,7 +2493,6 @@ "Type_your_job_title": "Калі ласка, увядзіце назву пасады", "Type_your_message": "Увядзіце ваша паведамленне", "Type_your_name": "Увядзіце сваё імя", - "Type_your_new_password": "Калі ласка, увядзіце новы пароль", "Type_your_password": "Калі ласка, увядзіце пароль", "Type_your_username": "Калі ласка, увядзіце імя карыстальніка", "UI_Allow_room_names_with_special_chars": "Дазволіць спецыяльныя сімвалы ў нумары імёнаў", @@ -2777,4 +2777,4 @@ "registration.component.form.invalidConfirmPass": "Пацвярджэнне пароля не супадае пароль", "registration.component.form.confirmPassword": "Пацвердзіць пароль", "registration.component.form.sendConfirmationEmail": "Адправіць па электроннай пошце пацвярджэнне" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json index 6e6c2ab85bd4..35a907b728d3 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Потвърждение на новата парола", "Confirm_New_Password_Placeholder": "Моля, въведете отново нова парола ...", "Confirm_password": "Потвърдите паролата", + "Confirm_your_password": "Потвърдите паролата", "Connection_Closed": "Връзката е затворена", "Connection_Reset": "Рестартиране на връзката", "Consulting": "консултативен", @@ -2470,7 +2471,6 @@ "Type_your_job_title": "Въведете длъжността си", "Type_your_message": "Въведете съобщението си", "Type_your_name": "Въведете името си", - "Type_your_new_password": "Въведете новата си парола", "Type_your_password": "Въведете паролата си", "Type_your_username": "Въведете потребителското си име", "UI_Allow_room_names_with_special_chars": "Позволете специални символи в имената на стаите", @@ -2751,4 +2751,4 @@ "registration.component.form.invalidConfirmPass": "Потвърждението на паролата не съвпада с паролата", "registration.component.form.confirmPassword": "Потвърдите паролата", "registration.component.form.sendConfirmationEmail": "Изпратете имейл за потвърждение" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json index 81e32ac96d21..4776295db449 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json @@ -488,6 +488,7 @@ "Confirm_new_password": "Potvrdi novu lozinku", "Confirm_New_Password_Placeholder": "Ponovno unesite novu zaporku ...", "Confirm_password": "Potvrdi svoju lozinku", + "Confirm_your_password": "Potvrdi svoju lozinku", "Connection_Closed": "Veza je zatvorena", "Connection_Reset": "Ponovno postavljanje veze", "Consulting": "savjetodavni", @@ -2466,7 +2467,6 @@ "Type_your_job_title": "Upišite svoj posao", "Type_your_message": "Upišite svoju poruku", "Type_your_name": "Upišite svoje ime", - "Type_your_new_password": "Upišite novu lozinku", "Type_your_password": "Upišite svoju lozinku", "Type_your_username": "Upišite svoje korisničko ime", "UI_Allow_room_names_with_special_chars": "Omogući posebne znakove u imenima soba", @@ -2747,4 +2747,4 @@ "registration.component.form.invalidConfirmPass": "Potvrda lozinke se ne slaže sa lozinkom", "registration.component.form.confirmPassword": "Potvrdi svoju lozinku", "registration.component.form.sendConfirmationEmail": "Pošalji potvrdni email" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json index 175c45a59ed0..ddb2d9d3e259 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json @@ -917,6 +917,7 @@ "Confirm_new_password": "Confirmar nova contrasenya", "Confirm_New_Password_Placeholder": "Si us plau, torni a ingressar una nova contrasenya ...", "Confirm_password": "Confirma la contrasenya", + "Confirm_your_password": "Confirma la contrasenya", "Confirmation": "Confirmació", "Connect": "Connectar", "Connected": "Connectat", @@ -2648,7 +2649,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Enviar missatges sense connexió d'LiveChat a un canal", "Omnichannel_on_hold_chat_resumed": "Represa de xat en espera: {{comment}}", "Omnichannel_on_hold_chat_automatically": "El xat es va reprendre automàticament des de En espera a l'rebre un nou missatge de {{guest}}", - "Omnichannel_on_hold_chat_manually": "El xat va ser reprès manualment des de En espera per {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "El xat va ser reprès manualment des de En espera per {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "El xat es va posar automàticament en espera perquè no hem rebut cap resposta de {{guest}} a {{timeout}} segons", "Omnichannel_On_Hold_manually": "El xat va ser posat manualment en espera per {{user}}", "Omnichannel_onHold_Chat": "Posar xat en espera", @@ -3856,7 +3857,6 @@ "Show_Avatars": "Mostra Avatars", "Show_counter": "Mostra comptador", "Show_email_field": "Mostra el camp de correu electrònic", - "Show_Message_In_Main_Thread": "Mostra els missatges del fil al fil principal", "Show_more": "Mostrar més", "Show_name_field": "Mostra el camp del nom", "show_offline_users": "Mostra els usuaris desconnectats", @@ -4300,7 +4300,6 @@ "Type_your_job_title": "Escriviu el vostre títol de treball", "Type_your_message": "Introduïu el missatge", "Type_your_name": "Escriu el teu nom", - "Type_your_new_password": "Escriviu la nova contrasenya", "Type_your_password": "Escriviu la vostra contrasenya", "Type_your_username": "Escriviu el vostre nom d'usuari", "UI_Allow_room_names_with_special_chars": "Permetre caràcters especials en noms de sales", @@ -4746,4 +4745,4 @@ "registration.component.form.sendConfirmationEmail": "Envia correu-e de confirmació", "RegisterWorkspace_Features_Marketplace_Title": "Mercat", "RegisterWorkspace_Features_Omnichannel_Title": "LiveChat" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json index 8bd4079ea77e..4d5b1232d9d2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json @@ -751,6 +751,7 @@ "Confirm_new_password": "Potvrďte nové heslo", "Confirm_New_Password_Placeholder": "Zadejte znovu nové heslo ...", "Confirm_password": "Potvrďte heslo", + "Confirm_your_password": "Potvrďte heslo", "Connect": "Připojit", "Connection_Closed": "Připojení bylo uzavřeno", "Connection_Reset": "Obnovit připojení", @@ -3241,7 +3242,6 @@ "Show_Avatars": "Zobrazit avatary", "Show_counter": "Zobrazit počítadlo", "Show_email_field": "Zobrazit pole email", - "Show_Message_In_Main_Thread": "Zobrazit zprávy vlákna i v hlavním vlákně", "Show_more": "Zobrazit více", "Show_name_field": "Zobrazit pole jméno", "show_offline_users": "zobrazit offline uživatele", @@ -3596,7 +3596,6 @@ "Type_your_job_title": "Zadejte svou pozici", "Type_your_message": "Napište zprávu", "Type_your_name": "Zadejte své jméno", - "Type_your_new_password": "Zadejte nové heslo", "Type_your_password": "Zadejte své heslo", "Type_your_username": "Zadejte své uživatelské jméno", "UI_Allow_room_names_with_special_chars": "Povolit speciální znaky v názvech místností", @@ -3980,4 +3979,4 @@ "registration.component.form.invalidConfirmPass": "Hesla nesouhlasí", "registration.component.form.confirmPassword": "Potvrďte heslo", "registration.component.form.sendConfirmationEmail": "Zaslat potvrzovací e-mail" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json index 0082ea09da14..0c3bb7349d5d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Cadarnhau cyfrinair Newydd", "Confirm_New_Password_Placeholder": "Ail-gofnodwch gyfrinair newydd ...", "Confirm_password": "Cadarnhau eich cyfrinair", + "Confirm_your_password": "Cadarnhau eich cyfrinair", "Connection_Closed": "Cysylltiad ar gau", "Connection_Reset": "Ailosodiad Cysylltiad", "Consulting": "Ymgynghori", @@ -2468,7 +2469,6 @@ "Type_your_job_title": "Teipiwch deitl eich swydd", "Type_your_message": "Teipiwch eich neges", "Type_your_name": "Teipiwch eich enw", - "Type_your_new_password": "Teipiwch eich cyfrinair newydd", "Type_your_password": "Teipiwch eich cyfrinair", "Type_your_username": "Teipiwch eich enw defnyddiwr", "UI_Allow_room_names_with_special_chars": "Caniatáu Cymeriadau Arbennig mewn Enwau Ystafelloedd", @@ -2750,4 +2750,4 @@ "registration.component.form.invalidConfirmPass": "Nid yw'r cadarnhad cyfrinair yn cyfateb i'r cyfrinair", "registration.component.form.confirmPassword": "Cadarnhau eich cyfrinair", "registration.component.form.sendConfirmationEmail": "Anfon ebost cadarnhad" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json index abf8e8119467..e45395e5ab8c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json @@ -755,6 +755,7 @@ "Confirm_new_password": "Bekræft ny adgangskode", "Confirm_New_Password_Placeholder": "Indtast venligst nyt kodeord igen...", "Confirm_password": "Bekræft dit kodeord", + "Confirm_your_password": "Bekræft dit kodeord", "Connect": "Forbind", "Connection_Closed": "Forbindelse lukket", "Connection_Reset": "Nulstilning af forbindelse", @@ -3260,7 +3261,6 @@ "Show_Avatars": "Vis avatars", "Show_counter": "Vis tæller", "Show_email_field": "Vis e-mail-feltet", - "Show_Message_In_Main_Thread": "Vis trådmeddelelser i hovedtråden", "Show_more": "Vis mere", "Show_name_field": "Vis navn felt", "show_offline_users": "Vis offline brugere", @@ -3617,7 +3617,6 @@ "Type_your_job_title": "Indtast din jobtitel", "Type_your_message": "Skriv din besked", "Type_your_name": "Indtast dit navn", - "Type_your_new_password": "Indtast din nye adgangskode", "Type_your_password": "Indtast dit kodeord", "Type_your_username": "Indtast dit brugernavn", "UI_Allow_room_names_with_special_chars": "Tillad særlige tegn i rumnavne", @@ -4002,4 +4001,4 @@ "registration.component.form.invalidConfirmPass": "Adgangskodebekræftelsen stemmer ikke overens med adgangskoden", "registration.component.form.confirmPassword": "Bekræft dit kodeord", "registration.component.form.sendConfirmationEmail": "Send bekræftelses-email" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json index 58c040ca09a1..465367b67ae8 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json @@ -490,6 +490,7 @@ "Confirm_new_password": "Bestätige neues Passwort", "Confirm_New_Password_Placeholder": "Bitte gib ein neues Passwort ein ...", "Confirm_password": "Bestätigen Sie Ihr Passwort.", + "Confirm_your_password": "Bestätigen Sie Ihr Passwort.", "Connection_Closed": "Verbindung geschlossen", "Connection_Reset": "Verbindung zurücksetzen", "Consulting": "Beratung", @@ -2476,7 +2477,6 @@ "Type_your_job_title": "Geben Sie Ihre Berufsbezeichnung ein", "Type_your_message": "Geben Sie Ihre Nachricht ein", "Type_your_name": "Geben Sie Ihren Namen ein", - "Type_your_new_password": "Geben Sie Ihr neues Passwort ein", "Type_your_password": "Geben Sie Ihr Passwort ein", "Type_your_username": "Gib deinen Benutzernamen ein", "UI_Allow_room_names_with_special_chars": "Erlaube Sonderzeichen in Raumnamen", @@ -2759,4 +2759,4 @@ "registration.component.form.invalidConfirmPass": "Die Passwörter stimmen nicht überein.", "registration.component.form.confirmPassword": "Bestätigen Sie Ihr Passwort.", "registration.component.form.sendConfirmationEmail": "Bestätigungsmail versenden" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json index a4ecf3c390a6..9645db821afc 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -9,6 +9,8 @@ "__usersCount__people_will_be_invited": "{{usersCount}} Mitglieder werden eingeladen", "__username__is_no_longer__role__defined_by__user_by_": "{{username}} ist nicht länger {{role}}, geändert durch {{user_by}}", "__username__was_set__role__by__user_by_": "{{username}} ist jetzt {{role}}, geändert durch {{user_by}}", + "removed__username__as__role_": "{{username}} als {{role}} entfernt", + "set__username__as__role_": "{{username}} als {{role}} gesetzt", "This_room_encryption_has_been_enabled_by__username_": "Die Verschlüsselung dieses Raums wurde aktiviert von {{username}}", "This_room_encryption_has_been_disabled_by__username_": "Die Verschlüsselung dieses Raums wurde deaktiviert von {{username}}", "Enabled_E2E_Encryption_for_this_room": "E2E-Verschlüsselung für diesen Raum aktiviert", @@ -1046,6 +1048,7 @@ "Confirm_new_password": "Bestätigen Sie ihr neues Passwort", "Confirm_New_Password_Placeholder": "Bitte geben Sie ein neues Passwort ein ...", "Confirm_password": "Bestätigen Sie Ihr Passwort", + "Confirm_your_password": "Bestätigen Sie Ihr Passwort", "Confirmation": "Bestätigung", "Configure_video_conference": "Telefonkonferenz konfigurieren", "Connect": "Verbinden", @@ -2008,16 +2011,22 @@ "You_do_not_have_permission_to_do_this": "Sie haben keine Berechtigung, dies zu tun", "Errors_and_Warnings": "Fehler und Warnungen", "Esc_to": "Esc: ", + "Estimated_wait_time": "Geschätzte Wartezeit", + "Estimated_wait_time_in_minutes": "Geschätzte Wartezeit (Zeit in Minuten)", "Event_Trigger": "Ereignisauslöser", "Event_Trigger_Description": "Bitte wählen Sie aus, welche Ereignistypen diesen ausgehenden Webhook auslösen", "every_5_minutes": "Einmal alle 5 Minuten", "every_10_seconds": "Einmal alle 10 Sekunden", + "every_30_seconds": "Einmal alle 30 Sekunden", + "every_10_minutes": "Einmal alle 10 Minuten", "every_30_minutes": "alle 30 Minuten", "every_day": "Einmal jeden Tag", "every_hour": "Stündlich", "every_minute": "Einmal pro Minute", "every_second": "Einmal jede Sekunde", "every_six_hours": "Alle 6 Stunden", + "every_12_hours": "Einmal alle 12 Stunden", + "every_24_hours": "Einmal alle 24 Stunden", "Everyone_can_access_this_channel": "Jeder kann auf diesen Kanal zugreifen", "Exact": "Genau", "Example_payload": "Beispiel-Payload", @@ -2151,6 +2160,8 @@ "Federation_Matrix_bridge_localpart": "AppService User Localpart", "Federation_Matrix_registration_file": "Registrierungsdatei", "Federation_Matrix_enable_typing_status": "\"Benutzer schreibt\"-Status anzeigen", + "Federation_Matrix_not_allowed_to_change_moderator": "Sie dürfen den Moderator nicht wechseln", + "Federation_Matrix_not_allowed_to_change_owner": "Sie dürfen den Besitzer nicht wechseln", "Field": "Feld", "Field_removed": "Feld entfernt", "Field_required": "Feld erforderlich", @@ -2497,6 +2508,7 @@ "initials_avatar": "Avatar aus Initialen", "Inline_code": "Inline-Code", "Install": "Installieren", + "Install_anyway": "Trotzdem installieren", "Install_Extension": "Erweiterung installieren", "Install_FxOs": "Installieren Sie Rocket.Chat auf Ihrem Firefox", "Install_FxOs_done": "Super! Nun lässt sich Rocket.Chat über das Icon auf dem Startbildschirm nutzen. Viel Spaß mit Rocket.Chat!", @@ -2744,7 +2756,9 @@ "Layout_Login_Terms": "Anmeldebedingungen", "Layout_Privacy_Policy": "Datenschutzbestimmungen", "Layout_Show_Home_Button": "\"Home-Button\" anzeigen", + "Layout_Home_Custom_Block_Visible": "Benutzerdefinierte Inhalte auf der Startseite anzeigen", "Layout_Custom_Body_Only": "Nur benutzerdefinierte Inhalte anzeigen", + "Layout_Custom_Body_Only_Description": "Dadurch werden alle anderen Inhaltsblöcke auf der Homepage ausgeblendet.", "Layout_Sidenav_Footer": "Seitenfußzeile", "Layout_Sidenav_Footer_description": "Die Größe der Fußzeile beträgt 260 x 70 Pixel.", "Layout_Sidenav_Footer_Dark_description": "Die Größe der Fußzeile beträgt 260 x 70 Pixel.", @@ -2967,7 +2981,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Offline-Nachrichten des Livechats an einen Kanal senden", "Omnichannel_on_hold_chat_resumed": "Chat aus Warteschleife wieder aufgenommen: {{comment}}", "Omnichannel_on_hold_chat_automatically": "Der Chat wurde automatisch aus der Warteschleife wieder aufgenommen, nachdem eine neue Nachricht von {{guest}} eingegangen ist", - "Omnichannel_on_hold_chat_manually": "Der Chat wurde von {{user}} manuell aus der Warteschleife wieder aufgenommen ", + "Omnichannel_on_hold_chat_resumed_manually": "Der Chat wurde von {{user}} manuell aus der Warteschleife wieder aufgenommen ", "Omnichannel_On_Hold_due_to_inactivity": "Der Chat wurde automatisch in die Warteschleife gestellt, weil wir innerhalb von {{timeout}} Sekunden keine Antwort von {{guest}} erhalten haben", "Omnichannel_On_Hold_manually": "Der Chat wurde von {{user}} manuell in die Warteschleife gestellt", "Omnichannel_onHold_Chat": "Chat in Warteschleife stellen", @@ -2985,6 +2999,7 @@ "Livechat_title_color": "Hintergrundfarbe des Livechat-Titels", "Livechat_transcript_already_requested_warning": "Das Protokoll dieses Chats wurde bereits angefordert und wird gesendet, sobald das Gespräch beendet ist.", "Livechat_transcript_has_been_requested": "Das Chatprotokoll wurde angefordert.", + "Livechat_email_transcript_has_been_requested": "Das Transkript wurde angefordert. Es kann ein paar Sekunden dauern.", "Livechat_transcript_request_has_been_canceled": "Die Anforderung des Chatprotokolls wurde storniert.", "Livechat_transcript_sent": "Omnichannel-Mitschrift versendet", "Livechat_transfer_return_to_the_queue": "{{from}} hat den Chat in die Warteschlange gestellt", @@ -3626,6 +3641,7 @@ "Only_invited_users_can_acess_this_channel": "Nur eingeladene Benutzer können diesem Channel beitreten", "Oops_page_not_found": "Hoppla! Seite nicht gefunden", "Oops!": "Hoppla!", + "Person_Or_Channel": "Person oder Channel", "Open": "Öffnen", "Open_call": "Anruf öffnen", "Open_call_in_new_tab": "Anruf in neuem Registerkarte öffnen", @@ -4001,6 +4017,7 @@ "Request_comment_when_closing_conversation": "Kommentar beim Schließen der Konversation anfordern", "Request_comment_when_closing_conversation_description": "Wenn dies aktiviert ist, muss der Agent einen Kommentar eingeben, bevor das Gespräch geschlossen wird.", "Request_tag_before_closing_chat": "Fordern Sie Tags an, bevor Sie die Unterhaltung beenden", + "request-pdf-transcript": "PDF-Transkript anfordern", "Requested_At": "Angefordert am", "Requested_By": "Angefordert von", "Require": "Anfordern", @@ -4011,11 +4028,13 @@ "Require_password_change": "Passwortänderung verlangen", "Resend_verification_email": "Bestätigungsmail erneut versenden", "Reset": "Zurücksetzen", + "Reset_priorities": "Prioritäten zurücksetzen", "Reset_Connection": "Verbindung zurücksetzen", "Reset_E2E_Key": "Ende-zu-Ende-Verlüsselungsschlüssel zurücksetzen", "Reset_password": "Passwort zurücksetzen", "Reset_section_settings": "Abschnittseinstellungen zurücksetzen", "Reset_TOTP": "TOTP zurücksetzen", + "Moderation_Reset_user_avatar": "Benutzer-Avatar zurücksetzen", "reset-other-user-e2e-key": "Ende-zu-Ende-Verschlüsselungsschlüssel eines anderen Nutzers zurücksetzen", "Responding": "Antwortet", "Response_description_post": "Leere Textteile oder Textteile mit einem leeren Textmerkmal werden einfach ignoriert. Nicht-200-Antworten werden mehrmals wiederholt. Eine Antwort wird unter dem oben angegebenen Aliasnamen und Avatar veröffentlicht. Sie können diese Informationen wie im obigen Beispiel überschreiben.", @@ -4024,6 +4043,7 @@ "Restart_the_server": "Server neu starten", "restart-server": "Server neu starten", "restart-server_description": "Berechtigung zum Neustart des Servers", + "Results": "Ergebnisse", "Resume": "Fortfahren", "Retail": "Handel", "Retention_setting_changed_successfully": "Die Einstellung für die Aufbewahrungsrichtlinie wurde erfolgreich geändert", @@ -4331,6 +4351,7 @@ "Send_Test_Email": "Test-E-Mail senden", "Send_via_email": "Per E-Mail senden", "Send_via_Email_as_attachment": "Per E-Mail als Anhang senden", + "Export_as_PDF": "Als PDF exportieren", "Send_Visitor_navigation_history_as_a_message": "Besucher-Navigationsprotokoll als Nachricht senden", "Send_visitor_navigation_history_on_request": "Besucher-Navigationsprotokoll auf Anfrage senden", "Send_welcome_email": "Willkommens-E-Mail senden", @@ -4398,7 +4419,6 @@ "Show_counter": "Zähler anzeigen", "Show_email_field": "E-Mail-Feld anzeigen", "Show_mentions": "Zeige Abzeichen für Erwähnungen", - "Show_Message_In_Main_Thread": "Thread-Meldungen im Hauptthread anzeigen", "Show_more": "Weitere Nutzer zeigen", "Show_name_field": "Namensfeld anzeigen", "show_offline_users": "Benutzer anzeigen, die offline sind", @@ -4887,7 +4907,6 @@ "Type_your_job_title": "Geben Sie Ihre Berufsbezeichnung ein", "Type_your_message": "Geben Sie Ihre Nachricht ein", "Type_your_name": "Geben Sie Ihren Namen ein", - "Type_your_new_password": "Geben Sie ein neues Passwort ein", "Type_your_password": "Geben Sie Ihr Passwort ein", "Type_your_username": "Geben Sie Ihren Benutzernamen ein", "UI_Allow_room_names_with_special_chars": "Sonderzeichen im Room-Namen erlauben", @@ -5561,6 +5580,9 @@ "RegisterWorkspace_Features_Omnichannel_Title": "Omnichannel", "RegisterWorkspace_Setup_Label": "Cloud-Account-E-Mail", "RegisterWorkspace_Setup_Email_Verification": "Bitte überprüfen Sie, ob der unten stehende Sicherheitscode mit dem in der E-Mail übereinstimmt.", + "RegisterWorkspace_Syncing_Error": "Beim Synchronisieren Ihres Workspace ist ein Fehler aufgetreten", + "RegisterWorkspace_Syncing_Complete": "Synchronisierung abgeschlossen", + "RegisterWorkspace_Connection_Error": "Beim Verbinden ist ein Fehler aufgetreten", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Ich bin mit den Nutzungsvereinbarung und den Datenschutzbestimmungen einverstanden", "Uninstall_grandfathered_app": "{{appName}} deinstallieren?" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json index 5bbda73f99b8..74c40707b40d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json @@ -495,6 +495,7 @@ "Confirm_new_password": "Επιβεβαιώστε τον καινούριο σας κωδικό", "Confirm_New_Password_Placeholder": "Πληκτρολογήστε ξανά νέο κωδικό πρόσβασης ...", "Confirm_password": "Επιβεβαιώστε τον κωδικό σας", + "Confirm_your_password": "Επιβεβαιώστε τον κωδικό σας", "Connection_Closed": "Η σύνδεση έκλεισε", "Connection_Reset": "Επαναφορά σύνδεσης", "Consulting": "Συμβουλευτικές υπηρεσίες", @@ -2481,7 +2482,6 @@ "Type_your_job_title": "Πληκτρολογήστε τον τίτλο εργασίας σας", "Type_your_message": "Πληκτρολογήστε το μήνυμά σας", "Type_your_name": "Πληκτρολογήστε το όνομά σας", - "Type_your_new_password": "Πληκτρολογήστε τον νέο κωδικό πρόσβασής σας", "Type_your_password": "Πληκτρολογήστε τον κωδικό πρόσβασης", "Type_your_username": "Πληκτρολογήστε το όνομα χρήστη σας", "UI_Allow_room_names_with_special_chars": "Αφήστε τους ειδικούς χαρακτήρες στα ονόματα των δωματίων", @@ -2768,4 +2768,4 @@ "registration.component.form.invalidConfirmPass": "Η επιβεβαίωση κωδικού δεν ταιριάζει με τον αρχικό κωδικό", "registration.component.form.confirmPassword": "Επιβεβαιώστε τον κωδικό σας", "registration.component.form.sendConfirmationEmail": "Αποστολή email επιβεβαίωσης" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index b2c18812828e..3690cbb353d7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -5911,4 +5911,4 @@ "Uninstall_grandfathered_app": "Uninstall {{appName}}?", "App_will_lose_grandfathered_status": "**This {{context}} app will lose its grandfathered status.** \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. Grandfathered apps count towards the limit but the limit is not applied to them.", "Theme_Appearence": "Theme Appearence" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json index 8f362b607b7c..8006cd075d7a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Konfirmu novan pasvorton", "Confirm_New_Password_Placeholder": "Bonvolu re-eniri novan pasvorton ...", "Confirm_password": "Konfirmu vian pasvorton", + "Confirm_your_password": "Konfirmu vian pasvorton", "Connection_Closed": "Ligo fermita", "Connection_Reset": "Rilato de konekto", "Consulting": "Konsultado", @@ -2474,7 +2475,6 @@ "Type_your_job_title": "Tajpu vian laborpostitolon", "Type_your_message": "Tajpu vian mesaĝon", "Type_your_name": "Tajpu vian nomon", - "Type_your_new_password": "Tajpu vian novan pasvorton", "Type_your_password": "Tajpu vian pasvorton", "Type_your_username": "Tajpu vian uzantnomon", "UI_Allow_room_names_with_special_chars": "Permesu Specialajn Karakterojn en Ĉambraj Nomoj", @@ -2760,4 +2760,4 @@ "registration.component.form.invalidConfirmPass": "La konfirmilo de pasvorto ne kongruas kun pasvorto", "registration.component.form.confirmPassword": "Konfirmu vian pasvorton", "registration.component.form.sendConfirmationEmail": "Sendu konfirman retpoŝton" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json index ace375b94c17..7e2219b43fb2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json @@ -2652,7 +2652,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Enviar mensajes fuera de línea de Livechat a un canal", "Omnichannel_on_hold_chat_resumed": "Reanudación del chat en espera: {{comment}}", "Omnichannel_on_hold_chat_automatically": "El chat se ha reanudado automáticamente desde En espera al recibir un nuevo mensaje de {{guest}}", - "Omnichannel_on_hold_chat_manually": "{{user}} ha reanudado el chat manualmente desde En espera ", + "Omnichannel_on_hold_chat_resumed_manually": "{{user}} ha reanudado el chat manualmente desde En espera ", "Omnichannel_On_Hold_due_to_inactivity": "El chat se ha puesto automáticamente en espera porque no hemos recibido ninguna respuesta de {{guest}} en {{timeout}} segundos", "Omnichannel_On_Hold_manually": "{{user}} ha puesto el chat en espera manualmente ", "Omnichannel_onHold_Chat": "Poner chat en espera", @@ -3891,7 +3891,6 @@ "Show_Avatars": "Mostrar avatares", "Show_counter": "Mostrar contador", "Show_email_field": "Mostrar campo de correo electrónico", - "Show_Message_In_Main_Thread": "Mostrar mensajes del hilo en el hilo principal", "Show_more": "Mostrar más", "Show_name_field": "Mostrar campo de nombre", "show_offline_users": "mostrar usuarios fuera de línea", @@ -4343,7 +4342,6 @@ "Type_your_job_title": "Escribe tu cargo", "Type_your_message": "Escribe tu mensaje", "Type_your_name": "Escribe tu nombre", - "Type_your_new_password": "Escribe la nueva contraseña", "Type_your_password": "Escribe tu contraseña", "Type_your_username": "Escribe tu nombre de usuario", "UI_Allow_room_names_with_special_chars": "Permitir caracteres especiales en nombres de Room", @@ -4897,4 +4895,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "Omnichannel", "RegisterWorkspace_Setup_Label": "Cuenta de correo electrónico en la nube", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Acepto los <1>términos y condiciones y la <3>política de privacidad" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json index 1f1356e23a32..c23985c99cbc 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json @@ -663,6 +663,7 @@ "Confirm_new_password": "تأیید رمز جدید", "Confirm_New_Password_Placeholder": "لطفا رمز عبور جدید را دوباره وارد کنید ...", "Confirm_password": "رمز عبور خود را تأیید کنید", + "Confirm_your_password": "رمز عبور خود را تأیید کنید", "Connect": "اتصال", "Connection_Closed": "اتصال بسته شد", "Connection_Reset": "تنظیم مجدد اتصال", @@ -2805,7 +2806,6 @@ "Type_your_job_title": "عنوان شغلی خود را تایپ کنید", "Type_your_message": "پیام خود را بنویسید", "Type_your_name": "نام خود را وارد نمایید", - "Type_your_new_password": "کلمه عبور جدید را وارد کنید", "Type_your_password": "رمز عبور خود را تایپ کنید", "Type_your_username": "نام کاربری خود را وارد کنید", "UI_Allow_room_names_with_special_chars": "اجازه کاراکترهای ویژه در نام اتاق", @@ -3105,4 +3105,4 @@ "registration.component.form.confirmPassword": "رمز عبور خود را تأیید کنید", "registration.component.form.sendConfirmationEmail": "ارسال ایمیل تایید", "RegisterWorkspace_Features_Omnichannel_Title": "کانال همه‌کاره" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index e65cb15ca561..f3deb4a3063e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -1059,6 +1059,7 @@ "Confirm_new_password": "Vahvista uusi salasana", "Confirm_New_Password_Placeholder": "Anna uusi salasana uudelleen...", "Confirm_password": "Vahvista salasanasi", + "Confirm_your_password": "Vahvista salasanasi", "Confirmation": "Vahvistus", "Configure_video_conference": "Määritä neuvottelupuhelu", "Connect": "Yhdistä", @@ -3013,7 +3014,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Lähetä Livechatin offline-viestit kanavalle", "Omnichannel_on_hold_chat_resumed": "Pidossa ollut keskustelu jatkuu: {{comment}}", "Omnichannel_on_hold_chat_automatically": "Keskustelu jatkui automaattisesti Pidossa-tilasta, kun uusi viesti saapui vieraalta {{guest}}", - "Omnichannel_on_hold_chat_manually": "{{user}} jatkoi keskustelua manuaalisesti Pidossa-tilasta", + "Omnichannel_on_hold_chat_resumed_manually": "{{user}} jatkoi keskustelua manuaalisesti Pidossa-tilasta", "Omnichannel_On_Hold_due_to_inactivity": "Keskustelu asetettiin pitoon automaattisesti, koska {{guest}} ei vastannut {{timeout}} sekuntiin", "Omnichannel_On_Hold_manually": "{{user}} asetti keskustelun pitoon manuaalisesti", "Omnichannel_onHold_Chat": "Aseta keskustelu pitoon", @@ -4495,7 +4496,6 @@ "Show_default_content": "Näytä oletussisältö", "Show_email_field": "Näytä sähköposti-kenttä", "Show_mentions": "Näytä mainintojen merkki", - "Show_Message_In_Main_Thread": "Näytä ketjun viestit pääketjussa", "Show_more": "Näytä lisää", "Show_name_field": "Näytä nimi-kenttä", "show_offline_users": "näytä offline-käyttäjät", @@ -5005,7 +5005,6 @@ "Type_your_job_title": "Kirjoita työtehtäväsi", "Type_your_message": "Kirjoita viestisi", "Type_your_name": "Kirjoita nimesi", - "Type_your_new_password": "Kirjoita uusi salasana", "Type_your_password": "Kirjoita salasanasi", "Type_your_username": "Kirjoita käyttäjätunnuksesi", "UI_Allow_room_names_with_special_chars": "Salli erikoismerkit huoneiden nimessä", @@ -5811,4 +5810,4 @@ "Uninstall_grandfathered_app": "Poistetaanko {{appName}}?", "App_will_lose_grandfathered_status": "**Tämä {{context}}sovellus menettää aikaisemmin käytetössä olleen sovelluksen tilansa.** \n \nYhteisöversion työtiloissa voi olla käytössä enintään {{limit}} {{context}} sovellusta. aikaisemmin Aikaisemmin käytössä olleet sovellukset lasketaan mukaan rajoitukseen, mutta rajoitusta ei sovelleta niihin.", "Theme_Appearence": "Teeman ulkoasu" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json index 86b3b1f093fa..5d956f706e85 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -925,6 +925,7 @@ "Confirm_new_password": "Confirmer le nouveau mot de passe", "Confirm_New_Password_Placeholder": "Entrez à nouveau le nouveau mot de passe...", "Confirm_password": "Confirmez votre mot de passe", + "Confirm_your_password": "Confirmez votre mot de passe", "Confirmation": "Confirmation", "Connect": "Connexion", "Connected": "Connecté", @@ -2668,7 +2669,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Envoyer des messages hors ligne Livechat à un canal", "Omnichannel_on_hold_chat_resumed": "Le chat en attente a repris : {{comment}}", "Omnichannel_on_hold_chat_automatically": "Le chat a été automatiquement repris lorsqu'un nouveau message a été reçu de {{guest}}", - "Omnichannel_on_hold_chat_manually": "Le chat a été repris manuellement par {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "Le chat a été repris manuellement par {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "Le chat a été automatiquement mis en attente car nous n'avons reçu aucune réponse de {{guest}} depuis {{timeout}} secondes", "Omnichannel_On_Hold_manually": "Le chat a été mis manuellement en attente par {{user}}", "Omnichannel_onHold_Chat": "Mettre le chat en attente", @@ -3917,7 +3918,6 @@ "Show_Avatars": "Afficher les avatars", "Show_counter": "Afficher le compteur", "Show_email_field": "Afficher le champ d'adresse e-mail", - "Show_Message_In_Main_Thread": "Afficher les messages du fil dans le fil principal", "Show_more": "Afficher plus", "Show_name_field": "Afficher le champ de nom", "show_offline_users": "afficher les utilisateur hors ligne", @@ -4374,7 +4374,6 @@ "Type_your_job_title": "Entrez le titre de votre poste", "Type_your_message": "Entrez votre message", "Type_your_name": "Entrez votre nom", - "Type_your_new_password": "Entrez votre nouveau mot de passe", "Type_your_password": "Entrez votre mot de passe", "Type_your_username": "Entrez votre nom d'utilisateur", "UI_Allow_room_names_with_special_chars": "Autoriser les caractères spéciaux dans les noms de salon", @@ -4945,4 +4944,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "Omnicanal", "RegisterWorkspace_Setup_Label": "E-mail du compte cloud", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "J'accepte les <1>Conditions d'utilisation et la <3>Politique de confidentialité" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json index 5c8eb5e7ab3b..ee0c52ca4a39 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json @@ -309,6 +309,7 @@ "Condensed": "מקובץ", "Computer": "מחשב", "Confirm_password": "אמת את הסיסמה שלך", + "Confirm_your_password": "אמת את הסיסמה שלך", "Connection_Closed": "החיבור נסגר", "Connection_Reset": "החיבור אופס", "Contact": "יצירת קשר", @@ -1348,7 +1349,6 @@ "Type_your_email": "הכנס את כתובת הדוא״ל שלך", "Type_your_message": "הקלד את ההודעה שלך", "Type_your_name": "הכנס את השם שלך", - "Type_your_new_password": "הכנס את הסיסמה החדשה שלך", "Type_your_password": "הכנס את הסיסמה שלך", "Type_your_username": "הכנס את שם המשתמש שלך", "UI_Click_Direct_Message": "לחץ לשליחת הודעה פרטית", @@ -1535,4 +1535,4 @@ "registration.component.form.invalidConfirmPass": "אימות הססמה אינו זהה לססמה", "registration.component.form.confirmPassword": "אמת את הסיסמה שלך", "registration.component.form.sendConfirmationEmail": "שליחת דוא״ל אימות" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json index a06cbfcb732d..0e74af00275e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json @@ -213,4 +213,4 @@ "We_are_offline_Sorry_for_the_inconvenience": "हम ऑफ़लाइन हैं। असुविधा के लिए खेद है।", "Yes": "हाँ", "You": "आप" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hi.i18n.json index f97b713276b4..c8d21e6638fc 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hi.i18n.json @@ -213,4 +213,4 @@ "We_are_offline_Sorry_for_the_inconvenience": "हम ऑफ़लाइन हैं। असुविधा के लिए खेद है।", "Yes": "हाँ", "You": "आप" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json index ded38dc7cc55..0db25d33568a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json @@ -570,6 +570,7 @@ "Confirm_new_password": "Potvrdi novu lozinku", "Confirm_New_Password_Placeholder": "Ponovno unesite novu zaporku ...", "Confirm_password": "Potvrdi svoju lozinku", + "Confirm_your_password": "Potvrdi svoju lozinku", "Connect": "Povezati", "Connection_Closed": "Veza je zatvorena", "Connection_Reset": "Ponovno postavljanje veze", @@ -2609,7 +2610,6 @@ "Type_your_job_title": "Upišite svoj posao", "Type_your_message": "Upišite svoju poruku", "Type_your_name": "Upišite svoje ime", - "Type_your_new_password": "Upišite novu lozinku", "Type_your_password": "Upišite svoju lozinku", "Type_your_username": "Upišite svoje korisničko ime", "UI_Allow_room_names_with_special_chars": "Omogući posebne znakove u imenima soba", @@ -2896,4 +2896,4 @@ "registration.component.form.invalidConfirmPass": "Potvrda lozinke se ne slaže sa lozinkom", "registration.component.form.confirmPassword": "Potvrdi svoju lozinku", "registration.component.form.sendConfirmationEmail": "Pošalji potvrdni email" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index c89cca11c021..6cbd626f7526 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -1022,6 +1022,7 @@ "Confirm_new_password": "Új jelszó megerősítése", "Confirm_New_Password_Placeholder": "Adja meg újra az új jelszót…", "Confirm_password": "Erősítse meg a jelszavát", + "Confirm_your_password": "Erősítse meg a jelszavát", "Confirmation": "Megerősítés", "Configure_video_conference": "Konferenciahívás beállítása", "Connect": "Kapcsolódás", @@ -2905,7 +2906,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Élő csevegés kapcsolat nélküli üzeneteinek küldése egy csatornára", "Omnichannel_on_hold_chat_resumed": "Várakoztatott csevegés folytatva: {{comment}}", "Omnichannel_on_hold_chat_automatically": "A csevegés automatikusan folytatódott a várakoztatásból, amikor új üzenetet kapott {{guest}} vendégtől", - "Omnichannel_on_hold_chat_manually": "A csevegést {{user}} kézileg folytatta a várakoztatásból", + "Omnichannel_on_hold_chat_resumed_manually": "A csevegést {{user}} kézileg folytatta a várakoztatásból", "Omnichannel_On_Hold_due_to_inactivity": "A csevegés automatikusan várakoztatásba lett helyezve, mivel nem kaptunk semmilyen választ {{guest}} vendégtől {{timeout}} másodperc alatt", "Omnichannel_On_Hold_manually": "A csevegést {{user}} kézileg várakoztatásba helyezte", "Omnichannel_onHold_Chat": "Csevegés várakoztatásba helyezése", @@ -4326,7 +4327,6 @@ "Show_counter": "Megjelölés olvasatlanként", "Show_email_field": "E-mail-cím mező megjelenítése", "Show_mentions": "Említések jelvényének megjelenítése", - "Show_Message_In_Main_Thread": "Szálüzenetek megjelenítése a főszálban", "Show_more": "Több megjelenítése", "Show_name_field": "Név mező megjelenítése", "show_offline_users": "kilépett felhasználók megjelenítése", @@ -4820,7 +4820,6 @@ "Type_your_job_title": "Írja be a munkakörét", "Type_your_message": "Írja be az üzenetét", "Type_your_name": "Írja be a nevét", - "Type_your_new_password": "Írja be az új jelszavát", "Type_your_password": "Írja be a jelszavát", "Type_your_username": "Írja be felhasználónevét", "UI_Allow_room_names_with_special_chars": "Különleges karakterek engedélyezése a szobanevekben", @@ -5488,4 +5487,4 @@ "Join_your_team": "Csatlakozás csapathoz", "Create_an_account": "Fiók létrehozása", "RegisterWorkspace_Features_Marketplace_Title": "Piactér" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json index 069f4183cd01..8a42c8f07318 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Konfirmasi password baru", "Confirm_New_Password_Placeholder": "Silakan masukkan kembali kata sandi baru ...", "Confirm_password": "Konfirmasikan kata sandi anda", + "Confirm_your_password": "Konfirmasikan kata sandi anda", "Connection_Closed": "Koneksi ditutup", "Connection_Reset": "Koneksi diatur ulang", "Consulting": "Konsultasi", @@ -2482,7 +2483,6 @@ "Type_your_job_title": "Ketik jabatan Anda", "Type_your_message": "Ketik pesan Anda", "Type_your_name": "Ketik nama Anda", - "Type_your_new_password": "Ketik password baru Anda", "Type_your_password": "Ketikkan kata sandi Anda", "Type_your_username": "Ketikkan nama pengguna Anda", "UI_Allow_room_names_with_special_chars": "Biarkan Karakter Khusus dalam Nama Kamar", @@ -2769,4 +2769,4 @@ "registration.component.form.invalidConfirmPass": "Kata sandi konfirmasi tidak cocok dengan kata sandi utama", "registration.component.form.confirmPassword": "Konfirmasikan kata sandi anda", "registration.component.form.sendConfirmationEmail": "Kirim email konfirmasi" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json index 6c421f389611..decd72db4baa 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json @@ -518,6 +518,7 @@ "Confirm_new_password": "Conferma la nuova password", "Confirm_New_Password_Placeholder": "Inserisci nuovamente la nuova password ...", "Confirm_password": "Conferma la tua password", + "Confirm_your_password": "Conferma la tua password", "Connect": "Connetti", "Connection_Closed": "Connessione chiusa", "Connection_Reset": "Reset della connessione", @@ -2567,7 +2568,6 @@ "Type_your_job_title": "Digita il titolo del tuo lavoro", "Type_your_message": "Inserisci la tua messaggio", "Type_your_name": "Inserire il proprio nome", - "Type_your_new_password": "Inserisci la nuova password", "Type_your_password": "Digita la tua password", "Type_your_username": "Inserisci il tuo nome utente", "UI_Allow_room_names_with_special_chars": "Consenti caratteri speciali nei nomi delle stanze", @@ -2863,4 +2863,4 @@ "registration.component.form.invalidConfirmPass": "La password di conferma non corrisponde con la password", "registration.component.form.confirmPassword": "Conferma la tua password", "registration.component.form.sendConfirmationEmail": "Invia email di conferma" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json index dd932e0b3886..18bd26b34c07 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -912,6 +912,7 @@ "Confirm_new_password": "新しいパスワードの確認", "Confirm_New_Password_Placeholder": "新しいパスワードを再入力してください...", "Confirm_password": "パスワードを確認", + "Confirm_your_password": "パスワードの確認", "Confirmation": "確認", "Connect": "接続", "Connected": "接続済み", @@ -2645,7 +2646,7 @@ "Livechat_OfflineMessageToChannel_enabled": "ライブチャットオフラインメッセージをチャネルに送信", "Omnichannel_on_hold_chat_resumed": "保留中のチャットが再開されました:{{comment}}", "Omnichannel_on_hold_chat_automatically": "{{guest}}から新しいメッセージを受信し、チャットが保留中から自動的に再開されました", - "Omnichannel_on_hold_chat_manually": " {{user}}がチャットを手動で保留中から再開しました", + "Omnichannel_on_hold_chat_resumed_manually": " {{user}}がチャットを手動で保留中から再開しました", "Omnichannel_On_Hold_due_to_inactivity": "{{guest}}から{{timeout}}秒間返信を受信しなかったため、チャットが自動的に保留中になりました", "Omnichannel_On_Hold_manually": "{{user}}がチャットを手動で保留中にしました", "Omnichannel_onHold_Chat": "チャットを保留中にする", @@ -3883,7 +3884,6 @@ "Show_Avatars": "アバターの表示", "Show_counter": "カウンターを表示", "Show_email_field": "メールフィールドを表示", - "Show_Message_In_Main_Thread": "メインスレッドにスレッドメッセージを表示", "Show_more": "さらに表示", "Show_name_field": "名前フィールドを表示", "show_offline_users": "オフラインユーザーを表示", @@ -4333,7 +4333,6 @@ "Type_your_job_title": "役職を入力", "Type_your_message": "メッセージを入力", "Type_your_name": "名前を入力してください", - "Type_your_new_password": "新しいパスワードを入力", "Type_your_password": "パスワードを入力", "Type_your_username": "ユーザー名を入力", "UI_Allow_room_names_with_special_chars": "Room名に特殊文字を許可", @@ -4895,4 +4894,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "オムニチャネル", "RegisterWorkspace_Setup_Label": "クラウドアカウントメール", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "<1>使用と<3>プライバシーポリシーに同意します" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json index c4c1a5b55b0d..e95360251310 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json @@ -692,6 +692,7 @@ "Confirm_new_password": "დაადასტურეთ ახალი პაროლი", "Confirm_New_Password_Placeholder": "გთხოვთ, ხელახლა შეიყვანოთ ახალი პაროლი ...", "Confirm_password": "დაადასტურეთ თქვენი პაროლი", + "Confirm_your_password": "დაადასტურეთ თქვენი პაროლი", "Connect": "დაკავშირება", "Connection_Closed": "კავშირი დაიხურა", "Connection_Reset": "კავშირის გადატვირთვა", @@ -3337,7 +3338,6 @@ "Type_your_job_title": "აკრიფეთ თქვენისამუშაოს დასახელება", "Type_your_message": "აკრიფეთ თქვენი შეტყობინება", "Type_your_name": "აკრიფეთ თქვენი სახელი", - "Type_your_new_password": "აკრიფეთ თქვენი ახალი პაროლი", "Type_your_password": "აკრიფეთ თქვენი პაროლი", "Type_your_username": "აკრიფეთ თქვენი მომხმარებლის სახელი", "UI_Allow_room_names_with_special_chars": "ოთახების სახელებში სპეციალური ნიშნების დაშვება", @@ -3683,4 +3683,4 @@ "registration.component.form.invalidConfirmPass": "პაროლის დასტური არ შეესაბამება პაროლს", "registration.component.form.confirmPassword": "დაადასტურეთ თქვენი პაროლი", "registration.component.form.sendConfirmationEmail": "დადასტურების ელ.ფოსტის გაგზავნა" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json index 607dbb646244..43f5ee0e1cd4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json @@ -647,6 +647,7 @@ "Confirm_new_password": "បញ្ជាក់​លេខសំងាត់​ថ្មី", "Confirm_New_Password_Placeholder": "សូមបញ្ចូលពាក្យសម្ងាត់ថ្មីម្តងទៀត ...", "Confirm_password": "បញ្ជាក់​ពាក្យ​សម្ងាត់", + "Confirm_your_password": "បញ្ជាក់​ពាក្យ​សម្ងាត់", "Connect": "ភ្ជាប់", "Connection_Closed": "ការភ្ជាប់បានបិទ", "Connection_Reset": "កំណត់ការភ្ជាប់ឡើងវិញ", @@ -2815,7 +2816,6 @@ "Type_your_job_title": "វាយចំណងជើងការងាររបស់អ្នក", "Type_your_message": "វាយសាររបស់អ្នក", "Type_your_name": "វាយបញ្ចូលឈ្មោះរបស់អ្នក", - "Type_your_new_password": "បញ្ចូលពាក្យសម្ងាត់ថ្មីរបស់អ្នក", "Type_your_password": "បញ្ចូលពាក្យសម្ងាត់របស់អ្នក", "Type_your_username": "បញ្ចូលឈ្មោះអ្នកប្រើរបស់អ្នក", "UI_Allow_room_names_with_special_chars": "អនុញ្ញាតតួអក្សរពិសេសក្នុងឈ្មោះបន្ទប់", @@ -3115,4 +3115,4 @@ "registration.component.form.invalidConfirmPass": "ពាក្យ​សម្ងាត់​បញ្ជាក់​មិន​ដូច​ពាក្យ​សម្ងាត់​បាន​បញ្ចូល​", "registration.component.form.confirmPassword": "បញ្ជាក់​ពាក្យ​សម្ងាត់", "registration.component.form.sendConfirmationEmail": "ផ្ញើរអ៊ីម៉ែល​បញ្ជាក់" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json index df08ed5b1be9..f660099b04c7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json @@ -793,6 +793,7 @@ "Confirm_new_password": "새 비밀번호 확인", "Confirm_New_Password_Placeholder": "새 비밀번호를 다시 입력하세요...", "Confirm_password": "비밀번호를 확인하세요", + "Confirm_your_password": "비밀번호를 확인하세요", "Connect": "연결", "Connection_Closed": "연결이 닫혔습니다.", "Connection_Reset": "연결 재설정", @@ -3300,7 +3301,6 @@ "Show_Avatars": "아바타 표시", "Show_counter": "미확인 메시지수 표시", "Show_email_field": "이메일 필드 표시", - "Show_Message_In_Main_Thread": "기본 스레드에 스레드 메시지 표시", "Show_more": "더보기", "Show_name_field": "이름 필드 표시", "show_offline_users": "오프라인 사용자 표시", @@ -3659,7 +3659,6 @@ "Type_your_job_title": "직책을 입력하세요.", "Type_your_message": "메시지를 입력하세요.", "Type_your_name": "이름을 입력하세요.", - "Type_your_new_password": "새 암호를 입력하세요.", "Type_your_password": "암호를 입력하세요.", "Type_your_username": "사용자명을 입력하세요.", "UI_Allow_room_names_with_special_chars": "대화방명에 특수문자 허용", @@ -4044,4 +4043,4 @@ "registration.component.form.invalidConfirmPass": "비밀번호가 일치하지 않습니다.", "registration.component.form.confirmPassword": "비밀번호를 확인하세요", "registration.component.form.sendConfirmationEmail": "확인 메일 보내기" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json index 562ca42944e5..da57c23dec1c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json @@ -488,6 +488,7 @@ "Confirm_new_password": "Şîfreya Nû ya piştrast bikin", "Confirm_New_Password_Placeholder": "Ji kerema xwe şîfreya nû ve nû bike ...", "Confirm_password": "تێپەڕەوشەکەت پشتڕاستکەوە", + "Confirm_your_password": "تێپەڕەوشەکەت پشتڕاستکەوە", "Connection_Closed": "Girêdana girêdayî ye", "Connection_Reset": "Girêdana veguhastinê", "Consulting": "Şêwirmendî", @@ -2466,7 +2467,6 @@ "Type_your_job_title": "Navekî karê xwe binivîse", "Type_your_message": "Mesaja we", "Type_your_name": "navê te Type", - "Type_your_new_password": "Şîfreya nû xwe binivîsin", "Type_your_password": "Şîfreya te binivîse", "Type_your_username": "Navê te binivîse", "UI_Allow_room_names_with_special_chars": "Li Niştimanî Nasnameyên Taybet Taybetî bide", @@ -2749,4 +2749,4 @@ "registration.component.form.invalidConfirmPass": "دووبارەکراوەی تێپەڕەوشە یەکناگرێتەوە لەگەڵ تێپەڕەوشە", "registration.component.form.confirmPassword": "تێپەڕەوشەکەت پشتڕاستکەوە", "registration.component.form.sendConfirmationEmail": "ئیمەیڵی پشتڕاستکردنەوە بنێرە" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json index 55fd1ececb18..25ea4a1dd26f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json @@ -506,6 +506,7 @@ "Confirm_new_password": "ຢືນ​ຢັນ​ລະ​ຫັດ​ຜ່ານ​ໃຫມ່", "Confirm_New_Password_Placeholder": "ກະລຸນາໃສ່ລະຫັດຜ່ານໃຫມ່ອີກເທື່ອຫນຶ່ງ ...", "Confirm_password": "ຢືນຢັນລະຫັດຜ່ານຂອງທ່ານ", + "Confirm_your_password": "ຢືນຢັນລະຫັດຜ່ານຂອງທ່ານ", "Connection_Closed": "ການເຊື່ອມຕໍ່ປິດ", "Connection_Reset": "ການຕັ້ງຄ່າການເຊື່ອມຕໍ່", "Consulting": "ການປຶກສາຫາລື", @@ -2510,7 +2511,6 @@ "Type_your_job_title": "ພິມຊື່ຕໍາແຫນ່ງວຽກຂອງທ່ານ", "Type_your_message": "ພິມຂໍ້ຄວາມຂອງທ່ານ", "Type_your_name": "ພິມຊື່ຂອງທ່ານ", - "Type_your_new_password": "ພິມລະຫັດຜ່ານໃຫມ່ຂອງທ່ານ", "Type_your_password": "ພິມລະຫັດຜ່ານຂອງທ່ານ", "Type_your_username": "ພິມຊື່ຜູ້ໃຊ້ຂອງທ່ານ", "UI_Allow_room_names_with_special_chars": "ອະນຸຍາດໃຫ້ຕົວອັກສອນພິເສດໃນຊື່ຫ້ອງ", @@ -2797,4 +2797,4 @@ "registration.component.form.invalidConfirmPass": "ການຢືນຢັນລະຫັດຜ່ານບໍ່ກົງກັບລະຫັດຜ່ານ", "registration.component.form.confirmPassword": "ຢືນຢັນລະຫັດຜ່ານຂອງທ່ານ", "registration.component.form.sendConfirmationEmail": "ສົ່ງອີເມວການຢືນຢັນ" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json index e2185988156d..43d780cd153d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json @@ -544,6 +544,7 @@ "Confirm_new_password": "Patvirtinti naują slaptažodį", "Confirm_New_Password_Placeholder": "Prašome dar kartą įvesti naują slaptažodį ...", "Confirm_password": "Patvirtinkite savo slaptažodį", + "Confirm_your_password": "Patvirtinkite savo slaptažodį", "Connection_Closed": "Ryšys uždarytas", "Connection_Reset": "Ryšio atstatymas", "Consulting": "Konsultavimas", @@ -2529,7 +2530,6 @@ "Type_your_job_title": "Įveskite savo pareigų pavadinimą", "Type_your_message": "Įveskite savo pranešimą", "Type_your_name": "Įveskite savo vardą", - "Type_your_new_password": "Įveskite naują slaptažodį", "Type_your_password": "Įveskite savo slaptažodį", "Type_your_username": "Įveskite savo vartotojo vardą", "UI_Allow_room_names_with_special_chars": "Leisti specialius simbolius kambario pavadinimuose", @@ -2814,4 +2814,4 @@ "registration.component.form.invalidConfirmPass": "Slaptažodžio patvirtinimas nesutampa su slaptažodžiu", "registration.component.form.confirmPassword": "Patvirtinkite savo slaptažodį", "registration.component.form.sendConfirmationEmail": "Siųsti patvirtinimo el. Laišką" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json index 727e93796368..b8e0f5d84a66 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json @@ -499,6 +499,7 @@ "Confirm_new_password": "Apstipriniet jauno paroli", "Confirm_New_Password_Placeholder": "Lūdzu, vēlreiz ievadiet jauno paroli ...", "Confirm_password": "Apstipriniet savu paroli", + "Confirm_your_password": "Apstipriniet savu paroli", "Connection_Closed": "Savienojums ir aizvērts", "Connection_Reset": "Savienojuma atiestatīšana", "Consulting": "Konsultēšana", @@ -2481,7 +2482,6 @@ "Type_your_job_title": "Ieraksti savu darba nosaukumu", "Type_your_message": "Ierakstiet savu ziņojumu", "Type_your_name": "Ierakstiet savu vārdu", - "Type_your_new_password": "Ierakstiet savu jauno paroli", "Type_your_password": "Ievadiet savu paroli", "Type_your_username": "Ievadiet savu lietotājvārdu", "UI_Allow_room_names_with_special_chars": "Atļaut īpašus simbolus istabu nosaukumos", @@ -2755,4 +2755,4 @@ "registration.component.form.invalidConfirmPass": "Paroles apstiprinājums neatbilst parolei", "registration.component.form.confirmPassword": "Apstipriniet savu paroli", "registration.component.form.sendConfirmationEmail": "Nosūtīt apstiprinājuma e-pastu" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json index 6f8e66867f3d..a78f07857973 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json @@ -488,6 +488,7 @@ "Confirm_new_password": "Шинэ нууц үг баталгаажуулах", "Confirm_New_Password_Placeholder": "Шинэ нууц үгээ дахин оруулна уу ...", "Confirm_password": "Нууц үгээ батлах", + "Confirm_your_password": "Нууц үгээ батлах", "Connection_Closed": "Холболт хаалттай байна", "Connection_Reset": "Холболтыг дахин тохируулах", "Consulting": "Зөвлөгөө өгөх", @@ -2467,7 +2468,6 @@ "Type_your_job_title": "Ажлын нэрээ оруулна уу", "Type_your_message": "Мессеж бичнэ үү", "Type_your_name": "Нэрээ оруулна уу", - "Type_your_new_password": "Шинэ нууц үгээ бичнэ үү", "Type_your_password": "Нууц үгээ бичнэ үү", "Type_your_username": "Хэрэглэгчийн нэрээ оруулна уу", "UI_Allow_room_names_with_special_chars": "Өрөөний нэрээр тусгай тэмдэгтүүдийг зөвшөөрөх", @@ -2748,4 +2748,4 @@ "registration.component.form.invalidConfirmPass": "Нууц үг баталгаажуулалт нь нууц үгтэй таарахгүй байна", "registration.component.form.confirmPassword": "Нууц үгээ батлах", "registration.component.form.sendConfirmationEmail": "Баталгаажуулах имэйл илгээх" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json index 6deca6ccab50..0749c30b656f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json @@ -488,6 +488,7 @@ "Confirm_new_password": "Sahkan Kata Laluan Baru", "Confirm_New_Password_Placeholder": "Sila masukkan semula kata laluan baru ...", "Confirm_password": "Sahkan kata laluan anda", + "Confirm_your_password": "Sahkan kata laluan anda", "Connection_Closed": "Sambungan ditutup", "Connection_Reset": "Tetap semula sambungan", "Consulting": "Perundingan", @@ -2481,7 +2482,6 @@ "Type_your_job_title": "Taipkan jawatan anda", "Type_your_message": "Taipkan mesej anda", "Type_your_name": "Taipkan nama anda", - "Type_your_new_password": "Taipkan kata laluan baru anda", "Type_your_password": "Taip kata laluan anda", "Type_your_username": "Taip nama pengguna anda", "UI_Allow_room_names_with_special_chars": "Benarkan Huruf Khas dalam Nama Bilik", @@ -2765,4 +2765,4 @@ "registration.component.form.invalidConfirmPass": "Pengesahan kata laluan tidak sepadan dengan kata laluan", "registration.component.form.confirmPassword": "Sahkan kata laluan anda", "registration.component.form.sendConfirmationEmail": "Hantar e-mel pengesahan" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json index 74fa0d3441b3..fdc52656a9c8 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json @@ -920,6 +920,7 @@ "Confirm_new_password": "Bevestig nieuw wachtwoord", "Confirm_New_Password_Placeholder": "Voer het nieuwe wachtwoord opnieuw in...", "Confirm_password": "Bevestig uw wachtwoord", + "Confirm_your_password": "Bevestig uw wachtwoord", "Confirmation": "Bevestiging", "Connect": "Verbinden", "Connected": "Verbonden", @@ -2662,7 +2663,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Stuur de Livechat offline berichten naar een kanaal", "Omnichannel_on_hold_chat_resumed": "Chat in wachtstand hervat: {{comment}}", "Omnichannel_on_hold_chat_automatically": "De chat werd automatisch hervat vanuit de wachtrij bij het ontvangen van een nieuw bericht van {{guest}}", - "Omnichannel_on_hold_chat_manually": "De chat is handmatig hervat vanuit de wachstand door {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "De chat is handmatig hervat vanuit de wachstand door {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "De chat is automatisch in de wacht gezet omdat we in {{timeout}} seconden geen antwoord hebben ontvangen van {{guest}}", "Omnichannel_On_Hold_manually": "De chat is handmatig in de wacht gezet door {{user}}", "Omnichannel_onHold_Chat": "Chat on-hold plaatsen", @@ -3910,7 +3911,6 @@ "Show_Avatars": "Toon Avatars", "Show_counter": "Toon teller", "Show_email_field": "Toon e-mailveld", - "Show_Message_In_Main_Thread": "Toon threadberichten in de hoofdthread", "Show_more": "Laat meer zien", "Show_name_field": "Naamveld tonen", "show_offline_users": "offline gebruikers tonen", @@ -4364,7 +4364,6 @@ "Type_your_job_title": "Typ uw functietitel", "Type_your_message": "Schrijf je bericht", "Type_your_name": "Typ je naam", - "Type_your_new_password": "Typ je nieuwe wachtwoord", "Type_your_password": "Typ je wachtwoord", "Type_your_username": "Typ je gebruikersnaam", "UI_Allow_room_names_with_special_chars": "Sta speciale tekens toe in kamernamen", @@ -4933,4 +4932,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "Omnichannel", "RegisterWorkspace_Setup_Label": "E-mailadres van cloudaccount", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Ik ga akkoord met de <1>Algemene voorwaarden en <3>Privacybeleid" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json index 0899857363cb..e3ce45d342d8 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -552,6 +552,7 @@ "Confirm_new_password": "Bekrefte nytt passord", "Confirm_New_Password_Placeholder": "Vennligst skriv nytt passord igjen ...", "Confirm_password": "Bekreft passordet ditt", + "Confirm_your_password": "Bekreft passordet ditt", "Connection_Closed": "Tilkoblingen er stengt", "Connection_Reset": "Tilbakestilling av tilkobling", "Consulting": "Consulting", @@ -2571,7 +2572,6 @@ "Type_your_job_title": "Skriv inn jobbtittel", "Type_your_message": "Skriv inn meldingen din", "Type_your_name": "Skriv inn navnet ditt", - "Type_your_new_password": "Skriv inn ditt nye passord", "Type_your_password": "Skriv inn passordet ditt", "Type_your_username": "Skriv inn brukernavnet ditt", "UI_Allow_room_names_with_special_chars": "Tillat spesialtegn i romnavn", @@ -2861,4 +2861,4 @@ "registration.component.form.invalidConfirmPass": "Passordbekreftelsen stemmer ikke overens med passordet", "registration.component.form.confirmPassword": "Bekreft passordet ditt", "registration.component.form.sendConfirmationEmail": "Send bekreftelses-e-post" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json index 7e38962f16a4..ee9d3c9a95f3 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -1003,6 +1003,7 @@ "Confirm_new_password": "Potwierdź nowe hasło", "Confirm_New_Password_Placeholder": "Wprowadź ponownie nowe hasło…", "Confirm_password": "Potwierdź hasło", + "Confirm_your_password": "Potwierdź hasło", "Confirmation": "Potwierdzenie", "Configure_video_conference": "Konfiguracja połączenia konferencyjnego", "Connect": "Połącz", @@ -2866,7 +2867,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Wyślij wiadomości offline Livechat-u do kanału", "Omnichannel_on_hold_chat_resumed": "Czat wznowiony: {{comment}}", "Omnichannel_on_hold_chat_automatically": "Czat został automatycznie wznowiony z trybu wstrzymania po otrzymaniu nowej wiadomości od {{guest}}", - "Omnichannel_on_hold_chat_manually": "Czat został ręcznie wznowiony z trybu wstrzymania przez {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "Czat został ręcznie wznowiony z trybu wstrzymania przez {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "Czat został automatycznie zawieszony, ponieważ nie otrzymaliśmy żadnej odpowiedzi od {{guest}} w ciągu {{timeout}} seconds", "Omnichannel_On_Hold_manually": "Czat został ręcznie zawieszony przez {{user}}", "Omnichannel_onHold_Chat": "Umieść w zawieszeniu", @@ -4258,7 +4259,6 @@ "Show_counter": "Pokaż licznik", "Show_email_field": "Pokaż pole e-mail", "Show_mentions": "Pokaż odznakę za wzmianki", - "Show_Message_In_Main_Thread": "Pokaż wiadomości wątku w głównym wątku", "Show_more": "Pokaż więcej", "Show_name_field": "Pokaż pole nazwy", "show_offline_users": "Pokaż użytkowników offline", @@ -4744,7 +4744,6 @@ "Type_your_job_title": "Wpisz swój tytuł pracy", "Type_your_message": "Wpisz wiadomość", "Type_your_name": "Wpisz swoje imię i nazwisko", - "Type_your_new_password": "Wprowadź nowe hasło", "Type_your_password": "Wpisz swoje hasło", "Type_your_username": "Wpisz swoją nazwę użytkownika", "UI_Allow_room_names_with_special_chars": "Zezwalaj na specjalne znaki w nazwach pokoi", @@ -5407,4 +5406,4 @@ "RegisterWorkspace_Setup_Label": "E-mail konta w chmurze", "RegisterWorkspace_Syncing_Complete": "Synchronizacja zakończona", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Zgadzam się z <1>zasadami i warunkami i <3>Polityką prywatności." -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index 705ee21c6c54..efbe4b426654 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -947,6 +947,7 @@ "Confirm_new_password": "Confirme a nova senha", "Confirm_New_Password_Placeholder": "Insira novamente a nova senha ...", "Confirm_password": "Confirmar a senha", + "Confirm_your_password": "Confirmar a senha", "Confirmation": "Confirmação", "Connect": "Conectar", "Connected": "Conectado", @@ -2697,7 +2698,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Envie mensagens offline do livechat para um canal", "Omnichannel_on_hold_chat_resumed": "Conversa em espera retomada: {{comment}}", "Omnichannel_on_hold_chat_automatically": "A conversa foi automaticamente retomada de em espera após receber uma nova mensagem de {{guest}}", - "Omnichannel_on_hold_chat_manually": "A conversa foi manualmente retomada de em espera por {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "A conversa foi manualmente retomada de em espera por {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "A conversa foi colocada em espera automaticamente porque não recebemos nenhuma resposta de {{guest}} em {{timeout}} segundos", "Omnichannel_On_Hold_manually": "A conversa foi colocada em espera manualmente por {{user}}", "Omnichannel_onHold_Chat": "Colocar conversa em espera", @@ -3950,7 +3951,6 @@ "Show_Avatars": "Mostrar avatares", "Show_counter": "Marcar como não lido", "Show_email_field": "Mostrar campo de e-mail", - "Show_Message_In_Main_Thread": "Exibir mensagens no tópico principal", "Show_more": "Mostrar mais", "Show_name_field": "Mostrar campo de nome", "show_offline_users": "mostrar usuários offline", @@ -4412,7 +4412,6 @@ "Type_your_job_title": "Digite o seu cargo", "Type_your_message": "Digite sua mensagem", "Type_your_name": "Digite seu nome", - "Type_your_new_password": "Digite sua nova senha", "Type_your_password": "Digite sua senha", "Type_your_username": "Digite seu nome", "UI_Allow_room_names_with_special_chars": "Permitir caracteres especiais em nomes de salas", @@ -4986,4 +4985,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "Omnichannel", "RegisterWorkspace_Setup_Label": "E-mail da conta da nuvem", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Eu concordo com os <1>Termos e condições e a <3>Política de privacidade" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json index 9a19691295c3..3e2ea1db9c0f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -623,6 +623,7 @@ "Confirm_new_password": "Confirme a nova palavra-passe", "Confirm_New_Password_Placeholder": "Por favor, escreva novamente a nova palavra-passe...", "Confirm_password": "Confirmar a senha", + "Confirm_your_password": "Confirmar a senha", "Connect": "Conectar", "Connection_Closed": "Conexão encerrada", "Connection_Reset": "Redefinir Conexão", @@ -2868,7 +2869,6 @@ "Type_your_job_title": "Digite o seu cargo", "Type_your_message": "Escreva a sua mensagem", "Type_your_name": "Escreva o seu nome", - "Type_your_new_password": "Escreva a sua nova palavra-passe", "Type_your_password": "Digite a sua senha", "Type_your_username": "Digite o seu nome de utilizador", "UI_Allow_room_names_with_special_chars": "Permitir caracteres especiais em nomes de salas", @@ -3183,4 +3183,4 @@ "registration.component.form.invalidConfirmPass": "A confirmação da senha não é igual à senha", "registration.component.form.confirmPassword": "Confirmar a senha", "registration.component.form.sendConfirmationEmail": "Enviar email de confirmação" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json index 4229282c38cb..4a9bf52c9456 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Confirma noua parola", "Confirm_New_Password_Placeholder": "Reintroduceți noua parolă ...", "Confirm_password": "Confirmați parola", + "Confirm_your_password": "Confirmați parola", "Connection_Closed": "Conexiunea este închisă", "Connection_Reset": "Resetarea conexiunii", "Consulting": "consultant", @@ -2472,7 +2473,6 @@ "Type_your_job_title": "Introduceți titlul postului", "Type_your_message": "Scrie mesajul", "Type_your_name": "Introduceți numele dvs.", - "Type_your_new_password": "Introduceți noua parolă", "Type_your_password": "Introduceți parola", "Type_your_username": "Introduceți numele de utilizator", "UI_Allow_room_names_with_special_chars": "Permiteți caracterele speciale în numele camerelor", @@ -2754,4 +2754,4 @@ "registration.component.form.invalidConfirmPass": "Confirmarea parolei nu se potrivește cu parola introdusă", "registration.component.form.confirmPassword": "Confirmați parola", "registration.component.form.sendConfirmationEmail": "Trimite email de confirmare" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index a3fb8ba56937..42f750b956db 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -1024,6 +1024,7 @@ "Confirm_new_password": "Подтвердите новый пароль", "Confirm_New_Password_Placeholder": "Повторно введите новый пароль ...", "Confirm_password": "Подтвердить пароль", + "Confirm_your_password": "Подтвердить пароль", "Confirmation": "Подтверждение", "Configure_video_conference": "Настройка звонков", "Connect": "Подключение", @@ -1404,6 +1405,7 @@ "Custom_User_Status_Updated_Successfully": "Пользовательский статус успешно обновлен", "Customer_without_registered_email": "У клиента нет зарегистрированного адреса электронной почты", "Customize": "Настроить", + "Customize_Content": "Настроить содержимое", "CustomSoundsFilesystem": "Хранилище пользовательских звуков", "Daily_Active_Users": "Активные пользователи за день", "Dashboard": "Панель", @@ -1474,6 +1476,7 @@ "Deployment": "Развертывание", "Description": "Описание", "Desktop": "Компьютер", + "Desktop_apps": "Приложения для настольных ПК", "Desktop_Notification_Test": "Проверка десктопных уведомлений", "Desktop_Notifications": "Уведомления на рабочем столе", "Desktop_Notifications_Default_Alert": "Стандартные оповещения на рабочем столе", @@ -1482,6 +1485,7 @@ "Desktop_Notifications_Duration_Description": "Количество секунд в течение которых отображаются уведомления на компьютере. Это может повлиять на центр уведомлений OS X. Введите 0, чтобы использовать настройки браузера по умолчанию и не влиять на центр уведомлений OS X.", "Desktop_Notifications_Enabled": "Уведомления на компьютере включены", "Desktop_Notifications_Not_Enabled": "Уведомления на рабочем столе не включены", + "Unselected_by_default": "Выбрано не по умолчанию", "Details": "Подробности", "Device_Management": "Управление устройствами", "Device_Management_Client": "Клиент", @@ -2131,6 +2135,7 @@ "Filter": "Фильтр", "Filter_by_category": "Фильтрация по категориям", "Filter_By_Price": "Фильтрация по цене", + "Filter_By_Status": "Фильтрация по статусу", "Filters": "Фильтры", "Filters_applied": "Примененные фильтры", "Financial_Services": "Финансовые услуги", @@ -2242,6 +2247,7 @@ "Hide_flextab": "Скрывать правую боковую панель по клику", "Hide_Group_Warning": "Вы уверены, что хотите спрятать группу \"%s\"?", "Hide_Livechat_Warning": "Вы уверены, что хотите спрятать Livechat с \"%s\"?", + "Hide_On_Workspace": "Скрыть в рабочем пространстве", "Hide_Private_Warning": "Вы уверены, что хотите спрятать беседу с \"%s\"?", "Hide_roles": "Скрывать роли пользователей", "Hide_room": "Скрыть комнату", @@ -2290,6 +2296,7 @@ "Iframe_X_Frame_Options_Description": "Параметры X-Frame-Options. [Вы можете посмотреть все опции здесь.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options#Syntax)", "Ignore": "Игнорировать", "Ignored": "Игнорируется", + "Ignore_Two_Factor_Authentication": "Игнорировать двухфакторную авторизацию", "Images": "Изображения", "IMAP_intercepter_already_running": "Перехватчик IMAP уже запущен", "IMAP_intercepter_Not_running": "Перехватчик IMAP не запущен", @@ -2382,6 +2389,7 @@ "Instructions_to_your_visitor_fill_the_form_to_send_a_message": "Инструкции для вашего посетителя заполнить форму, чтобы отправить сообщение", "Insert_Contact_Name": "Введите имя контакта", "Insert_Placeholder": "Вставить заполнитель", + "Install_rocket_chat_on_your_preferred_desktop_platform": "Установите Rocket.Chat на свой компьютер.", "Insurance": "Страхование", "Integration_added": "Интеграция была добавлена", "Integration_Advanced_Settings": "Дополнительные настройки", @@ -2459,6 +2467,7 @@ "Invitation_Subject_Default": "Вы были приглашены на [Site_Name]", "Invite": "Приглашение", "Invites": "Приглашения", + "Invite_and_add_members_to_this_workspace_to_start_communicating": "Приглашайте и добавляйте участников в это рабочее пространство, чтобы начать общение.", "Invite_Link": "Пригласительная ссылка", "link": "ссылка", "Invite_removed": "Приглашение удалено", @@ -2737,6 +2746,7 @@ "Lead_capture_email_regex": "Регулярное перемещение по электронной почте", "Lead_capture_phone_regex": "Повторное использование", "Least_recent_updated": "Наименее недавнее обновление", + "Learn_how_to_unlock_the_myriad_possibilities_of_rocket_chat": "Узнайте о всех возможностях Rocket.Chat.", "Leave": "Покинуть", "Leave_a_comment": "Оставить комментарий", "Leave_Group_Warning": "Вы уверены, что хотите покинуть группу \"%s\"?", @@ -2751,6 +2761,7 @@ "leave-p": "Оставить личные группы", "leave-p_description": "Разрешение покидать приватные группы", "Lets_get_you_new_one": "Давайте получим новый!", + "License": "Лицензия", "List_of_Channels": "Список чатов", "List_of_departments_for_forward": "Список департаментов, разрешенных к перенаправлению (необязательно)", "List_of_departments_for_forward_description": "Разрешить установить ограниченный список департаментов, которые могут принимать перенаправляемые чаты от данного департамента", @@ -2800,7 +2811,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Отправка оффлайн-сообщений Livechat в чат", "Omnichannel_on_hold_chat_resumed": "Чат в режиме удержания возобновлен: {{comment}}", "Omnichannel_on_hold_chat_automatically": "Чат был автоматически возобновлен из режима удержания при получении нового сообщения от {{guest}}", - "Omnichannel_on_hold_chat_manually": "Чат был вручную возобновлен из режима удержания пользователем {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "Чат был вручную возобновлен из режима удержания пользователем {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "Чат был автоматически поставлен на удержание, так как не было получено ответа от {{guest}} в течение {{timeout}} секунд", "Omnichannel_On_Hold_manually": "Чат был вручную поставлен на удержание пользователем {{user}}", "Omnichannel_onHold_Chat": "Поставить чат на удержание", @@ -2908,6 +2919,7 @@ "manage-assets_description": "Разрешение на управление ресурсами сервера", "manage-cloud": "Управление облаком", "manage-cloud_description": "Управление облаком", + "Manage_Devices": "Управление устройствами", "manage-email-inbox": "Управление входящей электронной почтой", "manage-email-inbox_description": "Разрешение на управление входящей электронной почтой", "manage-emoji": "Управление смайлами", @@ -3139,6 +3151,7 @@ "Mobex_sms_gateway_restful_address_desc": "IP или хост вашего Mobex REST API. Например. `http: //192.168.1.1:8080` или` https: //www.example.com:8080`", "Mobex_sms_gateway_username": "Имя пользователя", "Mobile": "Мобильные устройства", + "Mobile_apps": "Мобильные приложения", "mobile-upload-file": "Разрешить загрузку файлов на мобильные устройства", "Mobile_Push_Notifications_Default_Alert": "Уведомления на мобильных устройствах", "Moderation_Delete_message": "Удалить сообщение", @@ -3260,6 +3273,7 @@ "No_pages_yet_Try_hitting_Reload_Pages_button": "Пока нет страниц. Попробуйте нажать кнопку «Обновить страницы».", "No_pinned_messages": "Нет прикрепленных сообщений", "No_previous_chat_found": "Предыдущий чат не найден", + "No_requested_apps": "Нет запрошенных приложений", "No_results_found": "Ничего не найдено", "No_results_found_for": "Ничего не найдено:", "No_snippet_messages": "Нет сниппетов", @@ -3283,6 +3297,7 @@ "Not_likely": "Вряд ли", "Not_started": "Не начато", "Not_verified": "Не подтверждён", + "Not_Visible_To_Workspace": "Скрыто в рабочем пространстве", "Nothing": "Ничего", "Nothing_found": "Ничего не найдено", "Notice_that_public_channels_will_be_public_and_visible_to_everyone": "Обратите внимание, что публичные чаты будут публичными и видимыми для всех.", @@ -3526,6 +3541,7 @@ "Privacy": "Приватность", "Privacy_Policy": "Политика конфиденциальности", "Private": "Закрытый канал", + "Private_Apps": "Приватные приложения", "Private_Channel": "Закрытый канал", "Private_Channels": "Закрытый канал", "Private_Chats": "Приватные чаты", @@ -3591,8 +3607,8 @@ "Queue_delay_timeout": "Время ожидания задержки обработки очереди истекло", "Queue_Time": "Время очереди", "Queue_management": "Управление очередью", - "quote": "цитата", - "Quote": "цитата", + "quote": "цитировать", + "Quote": "Цитировать", "Random": "Случайный", "Rate_Limiter_Limit_RegisterUser": "Звонки с номера по умолчанию на ограничитель скорости для регистрации пользователя", "Rate_Limiter_Limit_RegisterUser_Description": "Количество вызовов по умолчанию для регистрации конечных точек пользователем (API REST и API реального времени), разрешенных в пределах временного диапазона, указанного в разделе \"Ограничение частоты запросов к API\".", @@ -3697,6 +3713,8 @@ "Request_comment_when_closing_conversation": "Запросить комментарий при закрытии разговора", "Request_comment_when_closing_conversation_description": "Если эта функция включена, то перед закрытием разговора агенту необходимо будет задать комментарий.", "Request_tag_before_closing_chat": "Запросить метки до закрытия разговора", + "Requested": "Запросы", + "Requested_apps_will_appear_here": "Запрошенные приложения появятся здесь", "Requested_At": "Запрошено в", "Requested_By": "Запрошено", "Require": "Требуется", @@ -3921,6 +3939,7 @@ "Script_Enabled": "Использовать скрипт", "Search": "Поиск", "Search_Apps": "Поиск приложений", + "Search_Requested_Apps": "Поиск запрошенных приложений", "Search_by_file_name": "Поиск по имени файла", "Search_by_username": "Поиск по логину", "Search_by_category": "Поиск по категории", @@ -3944,6 +3963,7 @@ "seconds": "секунды", "Secret_token": "Секретный токен", "Security": "Безопасность", + "See_documentation": "Открыть документацию", "See_full_profile": "Смотреть полный профиль", "See_on_Engagement_Dashboard": "Смотри на панели взаимодействия", "Select_a_department": "Выберите отдел", @@ -3963,7 +3983,9 @@ "Select_user": "Выберите пользователя", "Select_users": "Выберите пользователей", "Selected_agents": "Выбранные представители", + "Selected_by_default": "Выбрано по умолчанию", "Selected_departments": "Выбранные отделы", + "Selected_first_reply_unselected_following_replies": "Выбрано для первого ответа, не выбрано для последующих ответов", "Selected_monitors": "Выбранные мониторы", "Selecting_users": "Выбор пользователей", "Send": "Отправить", @@ -4053,18 +4075,19 @@ "Show_Avatars": "Показывать аватары", "Show_counter": "Показывать счетчик", "Show_email_field": "Показать поле электронной почты", - "Show_Message_In_Main_Thread": "Показывать сообщения треда в основном чате", "Show_more": "Показать больше", "Show_name_field": "Показать поле имени", "show_offline_users": "показывать офлайн пользователей", "Show_on_offline_page": "Показать на офлайн странице", "Show_on_registration_page": "Показывать на странице регистрации", "Show_only_online": "Показать только подключенных", + "Show_Only_This_Content": "Показать только это содержимое", "Show_preregistration_form": "Показать предварительную регистрационную форму", "Show_queue_list_to_all_agents": "Показывать список очередей всем представителям", "Show_room_counter_on_sidebar": "Показывать число комнат на боковой панели", "Show_Setup_Wizard": "Показывать мастер установки", "Show_the_keyboard_shortcut_list": "Показывать список горячих клавиш", + "Show_To_Workspace": "Показать в рабочем пространстве", "Show_video": "Показать видео", "Showing_archived_results": "

Показано %s архивных результатов

", "Showing_online_users": "Показано: {{total_showing}}. Подключенных: {{online}}. Всего: {{total}} пользователей", @@ -4096,8 +4119,8 @@ "Slash_Gimme_Description": "Показывает (つ ◕_◕) つ перед сообщением", "Slash_LennyFace_Description": "Показывает (͡ ° ͜ʖ ͡ °) после сообщения", "Slash_Shrug_Description": "Показывает ¯ \\ _ (ツ) _ / ¯ после сообщения", - "Slash_Status_Description": "Установить Ваше статусное сообщение", - "Slash_Status_Params": "Статусное сообщение", + "Slash_Status_Description": "Установить Ваш статус", + "Slash_Status_Params": "Ваш статус", "Slash_Tableflip_Description": "Показывает (╯ ° □ °) ╯( ┻━┻", "Slash_TableUnflip_Description": "Показывает ┬─┬ ノ (゜ - ゜ ノ)", "Slash_Topic_Description": "Установить тему", @@ -4126,6 +4149,7 @@ "Snippet_name": "Название сниппета", "Snippeted_a_message": "Создан сниппет {{snippetLink}}", "Social_Network": "Социальная сеть", + "Some_ideas_to_get_you_started": "Советы по началу работы", "Sorry_page_you_requested_does_not_exist_or_was_deleted": "Извините, запрошенная вами страница не существует или была удалена!", "Sort": "Сортировка", "Sort_By": "Сортировать по", @@ -4194,7 +4218,7 @@ "Stats_Total_Uploads_Size": "Общий размер загрузок", "Stats_Total_Users": "Всего пользователей", "Status": "Статус", - "StatusMessage": "Статусное Сообщение", + "StatusMessage": "Ваш Статус", "StatusMessage_Change_Disabled": "Администратор Вашего Rocket.Chat запретил изменение статусов", "StatusMessage_Changed_Successfully": "Статусное сообщения успешно изменено.", "StatusMessage_Placeholder": "Что Вы сейчас делаете?", @@ -4232,6 +4256,7 @@ "Tag_removed": "Метка удалена", "Tag_already_exists": "Метка уже существует", "Take_it": "Возьми это!", + "Take_rocket_chat_with_you_with_mobile_applications": "Установите Rocket.Chat в свой мобильный телефон.", "Taken_at": "Взят в", "Talk_Time": "Время разговора", "Target user not allowed to receive messages": "Целевому пользователю не разрешено получать сообщения", @@ -4514,7 +4539,6 @@ "Type_your_job_title": "Введите название своей должности", "Type_your_message": "Текст вашего сообщения", "Type_your_name": "Введите ваше имя", - "Type_your_new_password": "Введите ваш новый пароль", "Type_your_password": "Введите пароль", "Type_your_username": "Введите имя пользователя", "UI_Allow_room_names_with_special_chars": "Разрешить специальные символы в названии комнаты", @@ -4559,6 +4583,8 @@ "Unread_on_top": "Непрочитанное наверх", "Unread_Rooms": "Непрочитанные комнаты", "Unread_Rooms_Mode": "Режим непрочитанные комнаты", + "Unread_Requested_First": "Непрочитанные первые запросы", + "Unread_Requested_Last": "Непрочитанные последние запросы", "Unread_Tray_Icon_Alert": "Иконка уведомлений о непрочитанных сообщениях в трее", "Unstar_Message": "Убрать отметку", "Unmute_microphone": "Включить микрофон", @@ -4652,7 +4678,7 @@ "User_not_found": "Пользователь не найден", "User_not_found_or_incorrect_password": "Пользователь не найден или введен неверный пароль", "User_or_channel_name": "Имя пользователя или канала", - "User_Presence": "Присутствие пользователя", + "User_Presence": "Статус пользователя", "User_removed": "Пользователь удален", "User_removed_by": "Пользователь {{user_removed}} удален {{user_by}}.", "User_sent_a_message_on_channel": "{{username}} отправил сообщение в{{channel}}", @@ -4826,6 +4852,7 @@ "Viewing_room_administration": "Просмотр комнаты администрирования", "Visibility": "Видимость", "Visible": "Видимый", + "Visible_To_Workspace": "Видимый в рабочем пространстве", "Visit_Site_Url_and_try_the_best_open_source_chat_solution_available_today": "Посетите {{Site_URL}} и попробуйте лучшее решение для чата с открытым исходным кодом, доступное сегодня!", "Visitor": "Посетитель", "Visitor_Email": "Электронная почта посетителя", @@ -4925,6 +4952,7 @@ "Would_you_like_to_place_chat_on_hold": "Вы хотите поставить этот чат на удержание?", "Wrap_up_the_call": "Завершить звонок", "Wrap_Up_Notes": "Заключительные заметки", + "Workspace": "Рабочее пространство", "Yes": "Да", "Yes_archive_it": "Да, архивировать!", "Yes_clear_all": "Да, удалить все!", @@ -5096,10 +5124,26 @@ "onboarding.form.standaloneServerForm.servicesUnavailable": "Некоторые сервисы будут недоступны или потребуется ручная настройка", "onboarding.form.standaloneServerForm.publishOwnApp": "Чтобы отправлять push-уведомления, необходимо создать и опубликовать собственное приложение в Google Play и App Store", "onboarding.form.standaloneServerForm.manuallyIntegrate": "Необходимо вручную выполнить интеграцию с внешними сервисами", + "Theme_light": "Светлая", + "Theme_dark": "Темная", + "Theme_match_system": "Системная", + "Conversational_transcript": "Экспорт содержимого диалога", + "Always_send_the_transcript_to_contacts_at_the_end_of_the_conversations": "Всегда отправлять содержимое чата контактам по окончанию диалога.", + "Omnichannel_transcript_email": "Отправить содержимое диалога по электронной почте.", + "Accounts_Default_User_Preferences_omnichannelTranscriptEmail_Description": "Всегда отправлять содержимое чата контактам по окончанию диалога.", + "Omnichannel_transcript_pdf": "Экспорт содержимого чата в PDF.", + "Accounts_Default_User_Preferences_omnichannelTranscriptPDF_Description": "Всегда экспортировать содержимое чата по окончанию диалога.", + "User_Status": "Пользовательский статус", + "Presence_service": "Служба присутствия", + "New_custom_status": "Новый пользовательский статус", "Awaiting_confirmation": "Ожидает подтверждения", + "RegisterWorkspace_Registered_Description": "Доступные службы", "RegisterWorkspace_Features_MobileNotifications_Title": "Push-уведомления на мобильных устройствах", + "RegisterWorkspace_Features_MobileNotifications_Description": "Позволяет участникам рабочего пространства получать уведомления на своих мобильных устройствах.", "RegisterWorkspace_Features_Marketplace_Title": "Магазин", + "RegisterWorkspace_Features_Marketplace_Description": "Установите приложения в \"Магазине приложений\" Rocket.Chat для этого рабочего пространства.", "RegisterWorkspace_Features_Omnichannel_Title": "Настройки Omnichannel", "RegisterWorkspace_Setup_Label": "Адрес электронной почты учетной записи в облаке", - "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Я принимаю <1>Положения и условия и <3>Политику конфиденциальности" -} + "cloud.RegisterWorkspace_Setup_Terms_Privacy": "Я принимаю <1>Положения и условия и <3>Политику конфиденциальности", + "Theme_Appearence": "Внешний вид" +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json index e28c9cdb98e0..eb54defefe86 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json @@ -495,6 +495,7 @@ "Confirm_new_password": "Potvrďte nové heslo", "Confirm_New_Password_Placeholder": "Zadajte nové heslo znovu ...", "Confirm_password": "Potvrďte svoje heslo", + "Confirm_your_password": "Potvrďte svoje heslo", "Connection_Closed": "Pripojenie je ukončené", "Connection_Reset": "Reset pripojenia", "Consulting": "Konzultovanie", @@ -2482,7 +2483,6 @@ "Type_your_job_title": "Zadajte názov úlohy", "Type_your_message": "Zadajte svoju správu", "Type_your_name": "Zadajte svoje meno", - "Type_your_new_password": "Zadajte nové heslo", "Type_your_password": "Zadajte svoje heslo", "Type_your_username": "Zadajte svoje používateľské meno", "UI_Allow_room_names_with_special_chars": "Povoliť špeciálne znaky v názvoch miestností", @@ -2764,4 +2764,4 @@ "registration.component.form.invalidConfirmPass": "Potvrdenie hesla sa nezhoduje s heslom", "registration.component.form.confirmPassword": "Potvrďte svoje heslo", "registration.component.form.sendConfirmationEmail": "Pošlite potvrdzovací e-mail" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json index 89ae3ded22ec..954c54830717 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json @@ -487,6 +487,7 @@ "Confirm_new_password": "Potrdite novo geslo", "Confirm_New_Password_Placeholder": "Ponovno vnesite novo geslo ...", "Confirm_password": "Potrdi geslo", + "Confirm_your_password": "Potrdi geslo", "Connection_Closed": "Povezava je zaprta", "Connection_Reset": "Ponastavitev povezave", "Consulting": "Svetovanje", @@ -2462,7 +2463,6 @@ "Type_your_job_title": "Vnesite naslov dela", "Type_your_message": "Vnesite vaše sporočilo", "Type_your_name": "Vnesite vaše ime", - "Type_your_new_password": "Vnesite vaše novo gleslo", "Type_your_password": "Vnesite geslo", "Type_your_username": "Vnesite svoje uporabniško ime", "UI_Allow_room_names_with_special_chars": "V imenih sob dovoli posebne znake", @@ -2744,4 +2744,4 @@ "registration.component.form.invalidConfirmPass": "Potrditev gesla se ne ujema z geslom", "registration.component.form.confirmPassword": "Potrdi geslo", "registration.component.form.sendConfirmationEmail": "Pošlji potrditveno e-poštno sporočilo" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json index f8e1f75abd7d..fdd705dd663b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "Konfirmo fjalëkalimin e ri", "Confirm_New_Password_Placeholder": "Ri-futni fjalëkalimin e ri ...", "Confirm_password": "Konfirmoni fjalëkalimin tuaj", + "Confirm_your_password": "Konfirmoni fjalëkalimin tuaj", "Connection_Closed": "Lidhja u mbyll", "Connection_Reset": "Rivendosja e lidhjes", "Consulting": "këshillues", @@ -2472,7 +2473,6 @@ "Type_your_job_title": "Shkruani titullin e punës", "Type_your_message": "Shkruani mesazhin tuaj", "Type_your_name": "Shkruani emrin tuaj", - "Type_your_new_password": "Shkruaj fjalëkalimin tuaj të ri", "Type_your_password": "Shkruaj fjalëkalimin tënd", "Type_your_username": "Shkruaj emrin e përdoruesit", "UI_Allow_room_names_with_special_chars": "Lejo karaktere speciale në Emrat e dhomave", @@ -2755,4 +2755,4 @@ "registration.component.form.invalidConfirmPass": "Konfirmimi Fjalëkalimi nuk përputhet me fjalëkalimin", "registration.component.form.confirmPassword": "Konfirmoni fjalëkalimin tuaj", "registration.component.form.sendConfirmationEmail": "Dërgo email konfirmimi" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json index fc82e5e3190b..725a094d4e2c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json @@ -407,6 +407,7 @@ "Confirm_new_password": "Потврдите нову лозинку", "Confirm_New_Password_Placeholder": "Поново унесите нову лозинку ...", "Confirm_password": "Потврдите лозинку", + "Confirm_your_password": "Потврдите лозинку", "Connection_Closed": "Веза је затворена", "Connection_Reset": "Ресет везе", "Consulting": "Консалтинг", @@ -2295,7 +2296,6 @@ "Type_your_job_title": "Унесите свој назив посла", "Type_your_message": "Унесите поруку", "Type_your_name": "Типе иоур наме", - "Type_your_new_password": "Унесите нову лозинку", "Type_your_password": "Унесите своју лозинку", "Type_your_username": "Унесите своје корисничко име", "UI_Allow_room_names_with_special_chars": "Дозволи посебне знакове у именима соба", @@ -2565,4 +2565,4 @@ "registration.component.form.invalidConfirmPass": "Потврдна лозинка се не поклапа са лозинком", "registration.component.form.confirmPassword": "Потврдите лозинку", "registration.component.form.sendConfirmationEmail": "Пошаљи потврдну поруку" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index 4c49597530ea..873952ba420c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -1059,6 +1059,7 @@ "Confirm_new_password": "Bekräfta nytt lösenord", "Confirm_New_Password_Placeholder": "Vänligen skriv in nytt lösenord...", "Confirm_password": "Bekräfta ditt lösenord", + "Confirm_your_password": "Bekräfta ditt lösenord", "Confirmation": "Bekräftelse", "Configure_video_conference": "Konfigurera konferenssamtal", "Connect": "Anslut", @@ -3016,7 +3017,7 @@ "Livechat_OfflineMessageToChannel_enabled": "Skicka offlinemeddelanden för Livechat till en kanal ", "Omnichannel_on_hold_chat_resumed": "Parkerad chatt återupptogs: {{comment}}", "Omnichannel_on_hold_chat_automatically": "Chatten återupptogs automatiskt från parkerat läge när ett nytt meddelande från {{guest}} mottogs", - "Omnichannel_on_hold_chat_manually": "Chatten återupptogs manuellt från parkerat läge av {{user}}", + "Omnichannel_on_hold_chat_resumed_manually": "Chatten återupptogs manuellt från parkerat läge av {{user}}", "Omnichannel_On_Hold_due_to_inactivity": "Chatten parkerades manuellt eftersom {{guest}} inte svarade på {{timeout}} sekunder", "Omnichannel_On_Hold_manually": "Chatten parkerades manuellt av {{user}}", "Omnichannel_onHold_Chat": "Parkera chatten", @@ -4500,7 +4501,6 @@ "Show_default_content": "Visa standardinnehåll", "Show_email_field": "Visa e-postfältet", "Show_mentions": "Visa märke för omnämnanden", - "Show_Message_In_Main_Thread": "Visa trådmeddelanden i huvudtråden", "Show_more": "Visa mer", "Show_name_field": "Visa namnfältet", "show_offline_users": "visa offline-användare", @@ -5010,7 +5010,6 @@ "Type_your_job_title": "Skriv din jobbtitel", "Type_your_message": "Skriv in ditt meddelande", "Type_your_name": "Skriv in ditt namn", - "Type_your_new_password": "Skriv in ditt nya lösenord", "Type_your_password": "Skriv ditt lösenord", "Type_your_username": "Skriv ditt användarnamn", "UI_Allow_room_names_with_special_chars": "Tillåt särskilda tecken i rumsnamn", @@ -5816,4 +5815,4 @@ "Uninstall_grandfathered_app": "Avinstallera {{appName}}?", "App_will_lose_grandfathered_status": "**Denna {{context}}-app kommer att förlora sin status som gammal app.** \n \nArbetsytorna i Community Edition kan ha upp till {{limit}} __kontext__-appar aktiverade. Gamla appar inkluderas i gränsen, men gränsen tillämpas inte på dem.", "Theme_Appearence": "Utseende för tema" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json index 15a19ce66587..fb9d1cffe1af 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json @@ -489,6 +489,7 @@ "Confirm_new_password": "புதிய கடவு சொல்லை உறுதி செய்", "Confirm_New_Password_Placeholder": "புதிய கடவுச்சொல்லை மீண்டும் உள்ளிடுக ...", "Confirm_password": "உங்கள் கடவுச்சொல்லை உறுதிப்படுத்துக", + "Confirm_your_password": "உங்கள் கடவுச்சொல்லை உறுதிப்படுத்துக", "Connection_Closed": "இணைப்பு மூடப்பட்டது", "Connection_Reset": "இணைப்பு மீட்டமை", "Consulting": "ஆலோசனை", @@ -2473,7 +2474,6 @@ "Type_your_job_title": "உங்கள் பணிப் பெயரை தட்டச்சு செய்யவும்", "Type_your_message": "உங்கள் செய்தியைத் தட்டச்சு", "Type_your_name": "உங்கள் பெயரை தட்டச்சு", - "Type_your_new_password": "உங்கள் புதிய கடவுச்சொல்லை உள்ளிடவும்", "Type_your_password": "உங்கள் கடவுச்சொல்லை உள்ளிடவும்", "Type_your_username": "உங்கள் பயனர்பெயரைத் தட்டச்சு செய்க", "UI_Allow_room_names_with_special_chars": "அறை பெயர்களில் சிறப்பு எழுத்துகள் அனுமதி", @@ -2759,4 +2759,4 @@ "registration.component.form.invalidConfirmPass": "கடவுச்சொல்லை உறுதிப்படுத்தும் கடவுச்சொல் பொருந்தவில்லை", "registration.component.form.confirmPassword": "உங்கள் கடவுச்சொல்லை உறுதிப்படுத்துக", "registration.component.form.sendConfirmationEmail": "உறுதிப்படுத்தும் மின்னஞ்சல் அனுப்பவும்" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json index 00916ba37269..7574fb516b5f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json @@ -488,6 +488,7 @@ "Confirm_new_password": "ยืนยันรหัสผ่านใหม่", "Confirm_New_Password_Placeholder": "โปรดป้อนรหัสผ่านใหม่ ...", "Confirm_password": "ยืนยันรหัสผ่านของคุณ", + "Confirm_your_password": "ยืนยันรหัสผ่านของคุณ", "Connection_Closed": "ปิดการเชื่อมต่อ", "Connection_Reset": "รีเซ็ตการเชื่อมต่อ", "Consulting": "การให้คำปรึกษา", @@ -2464,7 +2465,6 @@ "Type_your_job_title": "พิมพ์ชื่องานของคุณ", "Type_your_message": "พิมพ์ข้อความของคุณ", "Type_your_name": "พิมพ์ชื่อของคุณ", - "Type_your_new_password": "พิมพ์รหัสผ่านใหม่", "Type_your_password": "พิมพ์รหัสผ่านของคุณ", "Type_your_username": "พิมพ์ชื่อผู้ใช้ของคุณ", "UI_Allow_room_names_with_special_chars": "อนุญาตให้ใช้อักขระพิเศษในชื่อห้อง", @@ -2742,4 +2742,4 @@ "registration.component.form.invalidConfirmPass": "การยืนยันรหัสผ่านไม่ตรงกับรหัสผ่าน", "registration.component.form.confirmPassword": "ยืนยันรหัสผ่านของคุณ", "registration.component.form.sendConfirmationEmail": "ส่งอีเมลยืนยัน" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json index d37f2c20769a..4b5a57995436 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json @@ -615,6 +615,7 @@ "Confirm_new_password": "Yeni Şifreyi Onayla", "Confirm_New_Password_Placeholder": "Lütfen yeni şifreyi tekrar girin...", "Confirm_password": "Parolanızı onaylayın", + "Confirm_your_password": "Parolanızı onaylayın", "Connect": "Bağlan", "Connection_Closed": "Bağlantı kapandı", "Connection_Reset": "Bağlantı sıfırlama", @@ -2948,7 +2949,6 @@ "Type_your_job_title": "İş başlığınızı yazın", "Type_your_message": "İletinizi yazın", "Type_your_name": "Adınızı yazın", - "Type_your_new_password": "Yeni şifrenizi yazın", "Type_your_password": "Şifrenizi giriniz", "Type_your_username": "Kullanıcı adınızı yazın", "UI_Allow_room_names_with_special_chars": "Oda Adlarında Özel Karakterlere İzin Ver", @@ -3271,4 +3271,4 @@ "registration.component.form.confirmPassword": "Parolanızı onaylayın", "registration.component.form.sendConfirmationEmail": "Doğrulama e-postası gönder", "RegisterWorkspace_Features_Omnichannel_Title": "Çoklu Kanal" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json index 66cfa374dd53..e72486a80a6f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json @@ -246,6 +246,7 @@ "Color": "رەڭ", "Commands": "كوماندا", "Confirm_password": "مەخپىي نومۇرنى جەزملەشتۈرۈش", + "Confirm_your_password": "مەخپىي نومۇرنى جەزملەشتۈرۈش", "Contact": "ئالاقىلىشىش", "Conversation": "پاراڭلىشىش", "Conversation_closed": "{{comment}} پاراڭلىشىش ئاخىرلاشتى", @@ -1092,7 +1093,6 @@ "Type_your_email": "سىزنىڭ ئىلخەت نومۇرىڭىزنى كىرگۈزۈڭ", "Type_your_message": "قالدۇرۇلغان سۆزنى كىرگۈزۈڭ", "Type_your_name": "ئىسمىڭىزنى كىرگۈزۈڭ", - "Type_your_new_password": "يېڭى پارولنى كىرگۈزۈڭ", "UI_DisplayRoles": "رولنى كۆرسىتىش", "UI_Merge_Channels_Groups": "خۇسۇسىي تەشكىلات ئېرىقلارنى بىرىكلەشتۈرۈش", "Unarchive": "تۈرگە ئايرىپ ئارخىپقا ساقلاشنى بىكار قىلىش", @@ -1243,4 +1243,4 @@ "registration.component.form.invalidConfirmPass": "ئىككىنچى قېتىم كىرگۈزۈلگەن مەخپىي نومۇر بىلەن بىرىنچى قېتىم كىرگۈزۈلگىنى بىلەن ماس كەلمىدى.", "registration.component.form.confirmPassword": "مەخپىي نومۇرنى جەزملەشتۈرۈش", "registration.component.form.sendConfirmationEmail": "جەزملەشتۈرگەن ئىلخەت يوللاندى" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json index 69cb5b30cc90..9919c95fc36e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json @@ -692,6 +692,7 @@ "Confirm_new_password": "Підтвердити новий пароль", "Confirm_New_Password_Placeholder": "Будь ласка, введіть новий пароль ще раз ...", "Confirm_password": "Підтвердити новий пароль", + "Confirm_your_password": "Підтвердити новий пароль", "Connect": "Підключення", "Connection_Closed": "З'єднання закрито", "Connection_Reset": "З'єднання скинуто", @@ -3043,7 +3044,6 @@ "Type_your_job_title": "Введіть назву вашої роботи", "Type_your_message": "текст повідомлення", "Type_your_name": "Введіть ваше ім'я", - "Type_your_new_password": "Введіть новий пароль", "Type_your_password": "Введіть свій пароль", "Type_your_username": "Введіть своє ім'я користувача", "UI_Allow_room_names_with_special_chars": "Дозволити спеціальні символи в іменах номерів", @@ -3360,4 +3360,4 @@ "registration.component.form.invalidConfirmPass": "Підтвердження пароля не збігаються пароль", "registration.component.form.confirmPassword": "Підтвердити новий пароль", "registration.component.form.sendConfirmationEmail": "Надіслати електронною поштою підтвердження" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json index 0038e5a708a1..d531bd034f62 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json @@ -584,6 +584,7 @@ "Confirm_new_password": "Xác nhận mật khẩu mới", "Confirm_New_Password_Placeholder": "Vui lòng nhập lại mật khẩu mới ...", "Confirm_password": "Xác nhận mật khẩu của bạn", + "Confirm_your_password": "Xác nhận mật khẩu của bạn", "Connection_Closed": "Kêt nôi bị đong", "Connection_Reset": "Đặt lại kết nối", "Consulting": "Tư vấn", @@ -2573,7 +2574,6 @@ "Type_your_job_title": "Loại chức vụ nghề nghiệp", "Type_your_message": "Nhập tin nhắn của bạn", "Type_your_name": "Gõ tên của bạn", - "Type_your_new_password": "Nhập mật khẩu mới của bạn", "Type_your_password": "Loại mật khẩu", "Type_your_username": "Nhập tên người dùng", "UI_Allow_room_names_with_special_chars": "Cho phép các ký tự đặc biệt trong tên phòng", @@ -2853,4 +2853,4 @@ "registration.component.form.invalidConfirmPass": "Xác nhận mật khẩu không khớp với mật khẩu", "registration.component.form.confirmPassword": "Xác nhận mật khẩu của bạn", "registration.component.form.sendConfirmationEmail": "Gửi email xác nhận" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json index f7f7ccffc322..ed085a4b9630 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json @@ -510,6 +510,7 @@ "Confirm_new_password": "确认新密码", "Confirm_New_Password_Placeholder": "请重新输入新密码...", "Confirm_password": "确认密码", + "Confirm_your_password": "确认密码", "Connection_Closed": "连接关闭", "Connection_Reset": "连接重置", "Consulting": "咨询", @@ -2497,7 +2498,6 @@ "Type_your_job_title": "输入你的职位", "Type_your_message": "输入你的讯息", "Type_your_name": "输入你的名字", - "Type_your_new_password": "输入您的新密码", "Type_your_password": "输入您的密码", "Type_your_username": "输入您的用户名", "UI_Allow_room_names_with_special_chars": "允许房间名称中的特殊字符", @@ -2774,4 +2774,4 @@ "registration.component.form.invalidConfirmPass": "第二次密码输入与第一次不匹配", "registration.component.form.confirmPassword": "确认密码", "registration.component.form.sendConfirmationEmail": "已发送确认电子邮件" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json index 97025f68b44a..a23e2152c5cf 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json @@ -911,6 +911,7 @@ "Confirm_new_password": "確認新密碼", "Confirm_New_Password_Placeholder": "請重新輸入新密碼...", "Confirm_password": "確認密碼", + "Confirm_your_password": "確認密碼", "Confirmation": "確認", "Connect": "連線", "Connected": "已連線", @@ -2576,7 +2577,7 @@ "Livechat_OfflineMessageToChannel_enabled": "將即時聊天離線消息傳送到頻道", "Omnichannel_on_hold_chat_resumed": "暫停聊天恢復:{{comment}}", "Omnichannel_on_hold_chat_automatically": "收到來自 {{guest}} 的新訊息後,聊天自動從 On Hold 恢復", - "Omnichannel_on_hold_chat_manually": "聊天是由 {{user}} 從 On Hold 手動恢復的", + "Omnichannel_on_hold_chat_resumed_manually": "聊天是由 {{user}} 從 On Hold 手動恢復的", "Omnichannel_On_Hold_due_to_inactivity": "因為我們在 {{timeout}} 秒內沒有收到來自 {{guest}} 的任何回覆,所以聊天被自動置於暫停狀態", "Omnichannel_On_Hold_manually": "{{user}} 手動將聊天置於暫停狀態", "Livechat_online": "即時聊天線上", @@ -3723,7 +3724,6 @@ "Show_Avatars": "顯示頭像", "Show_counter": "顯示櫃檯", "Show_email_field": "顯示電子郵件欄位", - "Show_Message_In_Main_Thread": "在主討論中顯示討論訊息", "Show_more": "顯示更多", "Show_name_field": "顯示名稱欄位", "show_offline_users": "顯示離線使用者", @@ -4107,7 +4107,6 @@ "Type_your_job_title": "輸入你的職位", "Type_your_message": "輸入您的訊息", "Type_your_name": "輸入您的姓名", - "Type_your_new_password": "輸入新密碼", "Type_your_password": "輸入您的密碼", "Type_your_username": "輸入您的使用者名稱", "UI_Allow_room_names_with_special_chars": "允許 Room 名稱中的特殊字元", @@ -4613,4 +4612,4 @@ "RegisterWorkspace_Features_Omnichannel_Title": "Omnichannel", "RegisterWorkspace_Setup_Label": "雲端帳戶電子郵件", "cloud.RegisterWorkspace_Setup_Terms_Privacy": "我同意<1>條款及條件和<3>隱私權政策" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json index b2150de8f157..17b890b82245 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json @@ -806,6 +806,7 @@ "Confirm_new_password": "确认新密码", "Confirm_New_Password_Placeholder": "请重新输入新密码...", "Confirm_password": "确认密码", + "Confirm_your_password": "确认密码", "Connect": "连接", "Connect_SSL_TLS": "使用 SSL/TLS 连接", "Connection_Closed": "连接关闭", @@ -3389,7 +3390,6 @@ "Show_Avatars": "显示头像", "Show_counter": "显示柜台", "Show_email_field": "显示电子邮件字段", - "Show_Message_In_Main_Thread": "在主讨论串中展示讨论串消息", "Show_more": "显示更多", "Show_name_field": "显示名称字段", "show_offline_users": "显示离线用户", @@ -3754,7 +3754,6 @@ "Type_your_job_title": "输入你的职位", "Type_your_message": "输入您的留言", "Type_your_name": "输入您的姓名", - "Type_your_new_password": "输入新密码", "Type_your_password": "输入您的密码", "Type_your_username": "输入您的用户名", "UI_Allow_room_names_with_special_chars": "允许Room名称中的特殊字符", @@ -4153,4 +4152,4 @@ "registration.component.form.invalidConfirmPass": "两次输入的密码不一致", "registration.component.form.confirmPassword": "确认密码", "registration.component.form.sendConfirmationEmail": "已发送确认电子邮件" -} +} \ No newline at end of file From e527f51f1a4ad681fbdf9cdf9ea2c66443a75cdb Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Mon, 26 Jun 2023 17:43:05 -0300 Subject: [PATCH 013/149] refactor: simplified canned response filter query (#29650) --- .../omnichannel/cannedResponses/CannedResponsesTable.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx index d179a14458b9..8a563e94bcc5 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx @@ -67,10 +67,7 @@ const CannedResponsesTable = () => { ); const getCannedResponses = useEndpoint('GET', '/v1/canned-responses'); - const { data, isLoading, isSuccess, refetch } = useQuery( - ['/v1/canned-responses', { debouncedText, sortDirection, itemsPerPage, current, sharing, createdBy }], - () => getCannedResponses(query), - ); + const { data, isLoading, isSuccess, refetch } = useQuery(['/v1/canned-responses', query], () => getCannedResponses(query)); const getTime = useFormatDateAndTime(); From 5cffdfcb97c48a778291e9155584a6f7e0dc7614 Mon Sep 17 00:00:00 2001 From: AdityaSingh-02 Date: Tue, 27 Jun 2023 11:03:46 +0530 Subject: [PATCH 014/149] Added how to use DB model pt 1 --- .tours/1---repository-overview.tour | 118 +++++++------- .tours/2---how-a-message-is-sent.tour | 190 +++++++++++----------- .tours/4---how-to-create-an-endpoint.tour | 2 +- .tours/5---how-to-create-a-db-model.tour | 2 +- .tours/6---how-to-use-a-db-model.tour | 40 +++++ 5 files changed, 196 insertions(+), 156 deletions(-) create mode 100644 .tours/6---how-to-use-a-db-model.tour diff --git a/.tours/1---repository-overview.tour b/.tours/1---repository-overview.tour index 5663b5631565..acdf7b9ccce0 100644 --- a/.tours/1---repository-overview.tour +++ b/.tours/1---repository-overview.tour @@ -1,60 +1,60 @@ -{ - "$schema": "https://aka.ms/codetour-schema", - "title": "1 - Repository-Overview", - "steps": [ - { - "file": "package.json", - "description": "### 1. Welcome to the Rocket.Chat Code Tour! This tour aims to provide an overview of the *Rocket.Chat* codebase, its architecture, key features and how things are working. \n\n### 2. The code tour is designed for the Everyone, whether you're a developer looking to contribute, a student learning web development, or simply curious about Rocket.Chat's inner workings.\n\n### 3. We'll explore specific modules, functionalities, or important sections of the codebase in a structured manner, allowing you to understand how Rocket.Chat works.", - "line": 2, - "title": "Welcome" - }, - { - "directory": "packages", - "description": "### packages Folder\nThe \"package\" folder in the Rocket Chat project contains sharable code that can be used by different projects within the Rocket Chat ecosystem. It houses reusable modules, libraries, or components that follow a modular approach to code organization. The shared code is managed as a package with its own versioning system, allowing projects to depend on specific versions. The folder may include documentation, examples, tests, and quality assurance processes to ensure reliability and ease of use. By organizing sharable code in this folder, Rocket Chat promotes code reuse, modularity, and collaboration across projects.\n\nYou can also Visit [Here](https://developer.rocket.chat/open-source-projects/server/repository-structure#directory-structure) for structure" - }, - { - "directory": "ee", - "description": "### ee (Enterprice Edition) Folder\nThe \"ee\" folder in the Rocket Chat project contains code, features, and functionalities exclusive to the Enterprise Edition of Rocket Chat. It provides additional features tailored for larger organizations, such as advanced security options, compliance features, enhanced administration tools, and integrations with enterprise systems.\n\n**Additional features**: The \"ee\" folder includes code files that implement extra features and functionalities exclusive to the enterprise edition. These features are designed to meet the requirements of enterprise customers, such as advanced security options, compliance features, enhanced administration tools, integrations with enterprise systems, and more." - }, - { - "directory": "apps/meteor", - "description": "### apps/meteor Folder\n#### The \"apps/meteor\" folder is a significant part of the codebase and contains important code and imports related to the Meteor framework in the Rocket Chat project.\n\n- **Folder structure**: The \"apps/meteor\" folder may have a structured organization, with subfolders representing different aspects of the application. For example:\n - **Client**: This subfolder may contain code specific to the client-side implementation, such as UI components, templates, stylesheets, and client-side libraries.\n - **Server**: This subfolder may contain code that runs on the server-side, handling server operations like database interactions, API endpoints, and server-side functions.\n - **Lib**: This subfolder may contain reusable code and utilities that can be shared between the client and server.\n - **Methods**: This subfolder may contain code for server-side methods, which provide an interface for client-side code to interact with the server and perform operations securely.\n - **Public**: This subfolder may contain publicly accessible files, such as static assets (images, fonts, etc.) that can be served directly to clients.\n - **Private**: This subfolder may contain private files and assets that are only accessible to the server-side code.", - "title": "apps/meteor" - }, - { - "directory": "apps/meteor/client", - "description": "### client Folder\n\n\nIn Rocket Chat, the \"apps/meteor/client\" directory refers to the client-side code that is specific to the Meteor framework within the Rocket Chat application. It contains frontend-related code and resources that are responsible for rendering and handling the user interface on the client side.\n" - }, - { - "directory": "apps/meteor/client/components", - "description": "### client/components Folder\n\n#### This folder contains reusable UI components. These components are modular and can be used in different parts of the application to provide consistent and reusable user interface elements.\n\n- **Reusable UI components**: The folder houses code files that define reusable UI components, such as buttons, input fields, modals, cards, avatars, tooltips, or any other user interface element that can be utilized in multiple parts of the Rocket Chat application.\n\n\n- **Component structure**: Each component typically consists of a JavaScript or TypeScript file that contains the component's logic and functionality. It may also include associated stylesheets, templates, or configuration files specific to that component.\n\n\n- **Composition and customization**: Components can often be composed together to build more complex UI elements. Developers can leverage the components in the folder to assemble larger, composite components that cater to specific features or requirements of the Rocket Chat application. Components may also provide options for customization through props or configuration parameters.\n\n- **Consistency and UI guidelines**: The components in the \"client/components\" folder adhere to established UI guidelines and design patterns within the Rocket Chat project. They help maintain consistency in the user interface across different parts of the application and ensure a cohesive and intuitive user experience.", - "title": "Client/components" - }, - { - "directory": "apps/meteor/client/lib", - "description": "### apps/meteor/client/lib/ Folder\n\n- ***This contains Code which can be used by both Server and Client part of application.***\n\n### A collection of objects that are reused on all of the client sides.\n#### This is to:\n- **Limit code duplication**\n- **Encourage contributors to use the code that is already existing in the codebase**\n- **Avoid re-implementing logic or re-create functions**" - }, - { - "directory": "apps/meteor/client/views", - "description": "### The Best place to Start (The FrontEnd)\n\n### *If you are a beginner looking to contribute to Rocket Chat, the \"client/views\" folder is an ideal starting point. It offers a clearer understanding of the application's inner workings by tracing module imports and observing component usage. By exploring this folder, you can learn how different components are structured, styled, and interact with each other. It provides valuable insights into the UI construction, facilitating improvements and feature additions. Starting your contribution journey in the \"client/views\" folder will enhance your understanding of the codebase and enable effective contributions to the project.*\n\n- **This is the Folder where a combination of multiple components comes together in action to build a single Rocket.Chat page is seen by client-side(Frontend) users.**\n\n- **The root view here can be seen in apps/meteor/client/views/root/AppRoot.tsx [here](./apps/meteor/client/views/root/AppRoot.tsx) where execution in the front begins**\n", - "title": "Best place for beginners" - }, - { - "file": "apps/meteor/client/views/root/AppRoot.tsx", - "description": "### The Root File\n\n- Rocket Chat is the starting point for the frontend execution, Since Rocket chat's Frontend is build on React hence it has a Root from where execution Starts. It initializes the application and establishes the layout. It plays a crucial role in setting up the initial structure and behavior of the frontend.\n\n- If You want to deeply understand how things are working you can trace elements and methods used in here", - "line": 12 - }, - { - "directory": "apps/meteor/private", - "description": "### Private Directory\n\n- All files inside a top-level directory called **private/** are only accessible from server code and can be loaded via the Assets API. This can be used for private data files and any files that are in your project directory that you don’t want to be accessible from the outside." - }, - { - "directory": "apps/meteor/public", - "description": "### Public Directory\n\n- All files inside a top-level directory called **public/** are served as-is to the client.\n- It can be accessed by everyone and can be imported directly" - }, - { - "directory": "apps/meteor/server", - "description": "### Server Directory\n\n- Server/ Directory is never loaded on the client side, People usually like to call it as backend and in Rocket.chat we use **NodeJs** as backend.\n- Most of the API Endpoints are created here and stored here through Database and in Rocket.chat we use **MongoDB** as Database\n- Any sensitive code that you don’t want served to the client, such as code containing passwords or authentication mechanisms, should be kept in the server/ directory.\n- *There are more Folders/Directories named as Server, Just remember all of them are servers*" - } - ] +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "1 - Repository-Overview", + "steps": [ + { + "file": "package.json", + "description": "### 1. Welcome to the Rocket.Chat Code Tour! This tour aims to provide an overview of the *Rocket.Chat* codebase, its architecture, key features and how things are working. \n\n### 2. The code tour is designed for the Everyone, whether you're a developer looking to contribute, a student learning web development, or simply curious about Rocket.Chat's inner workings.\n\n### 3. We'll explore specific modules, functionalities, or important sections of the codebase in a structured manner, allowing you to understand how Rocket.Chat works.", + "line": 2, + "title": "Welcome" + }, + { + "directory": "packages", + "description": "### packages \nThe \"package\" folder in the Rocket Chat project contains sharable code that can be used by different projects within the Rocket Chat ecosystem. It houses reusable modules, libraries, or components that follow a modular approach to code organization. The shared code is managed as a package with its own versioning system, allowing projects to depend on specific versions. The folder may include documentation, examples, tests, and quality assurance processes to ensure reliability and ease of use. By organizing sharable code in this folder, Rocket Chat promotes code reuse, modularity, and collaboration across projects.\n\nYou can also Visit [Here](https://developer.rocket.chat/open-source-projects/server/repository-structure#directory-structure) for structure" + }, + { + "directory": "ee", + "description": "### ee (Enterprice Edition) \nThe \"ee\" folder in the Rocket Chat project contains code, features, and functionalities exclusive to the Enterprise Edition of Rocket Chat. It provides additional features tailored for larger organizations, such as advanced security options, compliance features, enhanced administration tools, and integrations with enterprise systems.\n\n**Additional features**: The \"ee\" folder includes code files that implement extra features and functionalities exclusive to the enterprise edition. These features are designed to meet the requirements of enterprise customers, such as advanced security options, compliance features, enhanced administration tools, integrations with enterprise systems, and more." + }, + { + "directory": "apps/meteor", + "description": "### apps/meteor \n#### The \"apps/meteor\" folder is a significant part of the codebase and contains important code and imports related to the Meteor framework in the Rocket Chat project.\n\n- **Folder structure**: The \"apps/meteor\" folder may have a structured organization, with subfolders representing different aspects of the application. For example:\n - **Client**: This subfolder may contain code specific to the client-side implementation, such as UI components, templates, stylesheets, and client-side libraries.\n - **Server**: This subfolder may contain code that runs on the server-side, handling server operations like database interactions, API endpoints, and server-side functions.\n - **Lib**: This subfolder may contain reusable code and utilities that can be shared between the client and server.\n - **Methods**: This subfolder may contain code for server-side methods, which provide an interface for client-side code to interact with the server and perform operations securely.\n - **Public**: This subfolder may contain publicly accessible files, such as static assets (images, fonts, etc.) that can be served directly to clients.\n - **Private**: This subfolder may contain private files and assets that are only accessible to the server-side code.", + "title": "apps/meteor" + }, + { + "directory": "apps/meteor/client", + "description": "### apps/meteor/client \n\n\nIn Rocket Chat, the \"apps/meteor/client\" directory refers to the client-side code that is specific to the Meteor framework within the Rocket Chat application. It contains frontend-related code and resources that are responsible for rendering and handling the user interface on the client side.\n" + }, + { + "directory": "apps/meteor/client/components", + "description": "### client/components \n\n#### This folder contains reusable UI components. These components are modular and can be used in different parts of the application to provide consistent and reusable user interface elements.\n\n- **Reusable UI components**: The folder houses code files that define reusable UI components, such as buttons, input fields, modals, cards, avatars, tooltips, or any other user interface element that can be utilized in multiple parts of the Rocket Chat application.\n\n\n- **Component structure**: Each component typically consists of a JavaScript or TypeScript file that contains the component's logic and functionality. It may also include associated stylesheets, templates, or configuration files specific to that component.\n\n\n- **Composition and customization**: Components can often be composed together to build more complex UI elements. Developers can leverage the components in the folder to assemble larger, composite components that cater to specific features or requirements of the Rocket Chat application. Components may also provide options for customization through props or configuration parameters.\n\n- **Consistency and UI guidelines**: The components in the \"client/components\" folder adhere to established UI guidelines and design patterns within the Rocket Chat project. They help maintain consistency in the user interface across different parts of the application and ensure a cohesive and intuitive user experience.", + "title": "Client/components" + }, + { + "directory": "apps/meteor/client/lib", + "description": "### apps/meteor/client/lib/ \n\n- ***This contains Code which can be used by both Server and Client part of application.***\n\n### A collection of objects that are reused on all of the client sides.\n#### This is to:\n- **Limit code duplication**\n- **Encourage contributors to use the code that is already existing in the codebase**\n- **Avoid re-implementing logic or re-create functions**" + }, + { + "directory": "apps/meteor/client/views", + "description": "### The Best place to Start (The FrontEnd)\n\n### *If you are a beginner looking to contribute to Rocket Chat, the \"client/views\" folder is an ideal starting point. It offers a clearer understanding of the application's inner workings by tracing module imports and observing component usage. By exploring this folder, you can learn how different components are structured, styled, and interact with each other. It provides valuable insights into the UI construction, facilitating improvements and feature additions. Starting your contribution journey in the \"client/views\" folder will enhance your understanding of the codebase and enable effective contributions to the project.*\n\n- **This is the Folder where a combination of multiple components comes together in action to build a single Rocket.Chat page is seen by client-side(Frontend) users.**\n\n- **The root view here can be seen in apps/meteor/client/views/root/AppRoot.tsx [here](./apps/meteor/client/views/root/AppRoot.tsx) where execution in the front begins**\n", + "title": "Best place for beginners" + }, + { + "file": "apps/meteor/client/views/root/AppRoot.tsx", + "description": "### The Root File\n\n- Rocket Chat is the starting point for the frontend execution, Since Rocket chat's Frontend is build on React hence it has a Root from where execution Starts. It initializes the application and establishes the layout. It plays a crucial role in setting up the initial structure and behavior of the frontend.\n\n- If You want to deeply understand how things are working you can trace elements and methods used in here", + "line": 12 + }, + { + "directory": "apps/meteor/private", + "description": "### Private Directory\n\n- All files inside a top-level directory called **private/** are only accessible from server code and can be loaded via the Assets API. This can be used for private data files and any files that are in your project directory that you don’t want to be accessible from the outside." + }, + { + "directory": "apps/meteor/public", + "description": "### Public Directory\n\n- All files inside a top-level directory called **public/** are served as-is to the client.\n- It can be accessed by everyone and can be imported directly" + }, + { + "directory": "apps/meteor/server", + "description": "### Server Directory\n\n- Server/ Directory is never loaded on the client side, People usually like to call it as backend and in Rocket.chat we use **NodeJs** as backend.\n- Most of the API Endpoints are created here and stored here through Database and in Rocket.chat we use **MongoDB** as Database\n- Any sensitive code that you don’t want served to the client, such as code containing passwords or authentication mechanisms, should be kept in the server/ directory.\n- *There are more Folders/Directories named as Server, Just remember all of them are servers*" + } + ] } \ No newline at end of file diff --git a/.tours/2---how-a-message-is-sent.tour b/.tours/2---how-a-message-is-sent.tour index 64f692ce3833..bae6fcc125c7 100644 --- a/.tours/2---how-a-message-is-sent.tour +++ b/.tours/2---how-a-message-is-sent.tour @@ -1,96 +1,96 @@ -{ - "$schema": "https://aka.ms/codetour-schema", - "title": "2 - How a Message is sent (Client side)", - "steps": [ - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "## How a Message is Sent\n\n### *In this guide, we will explore the protocols, technologies, and tools involved in message sending. By understanding the fundamentals, you will gain confidence in navigating through the repository, Hence you will get more precise understanding of how a message is sent and what are methods being used, how are API calls made and much more. Let's embark on this exciting journey together to uncover the wonders of message sending!*", - "line": 1 - }, - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "## The Chat Room/Channel\n\n- **RoomBody.tsx** is a file that handles the rendering of the Chat Room/Channel on the client side. It brings together multiple components to enable user interaction and communication within the chat environment.\n\n- In this file, there are various methods implemented. Let's specifically examine the process of sending a message through the **MessageComposer** component.\n#### **Additionally, the code in \"RoomBody.tsx\" includes logic for performing various actions related to the chat functionality. These actions might include *sending messages*, *editing* or *deleting* messages, *managing user permissions*, and handling user interactions within the chat environment.**", - "line": 53 - }, - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "### Chat/Room Layout\n\n- The \"RoomBody.tsx\" file is straightforward to comprehend. You can easily navigate through the components and grasp their functionalities.\n\n- The majority of the code in this file revolves around message rendering and handling various chat-related actions.", - "line": 539, - "selection": { - "start": { - "line": 464, - "character": 15 - }, - "end": { - "line": 464, - "character": 19 - } - } - }, - { - "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", - "description": "### Composer Container\n\n- At the bottom of the \"RoomBody.tsx\" file, there is a component responsible for rendering the Message Composer. It accepts several props, including the room ID and subscription details. These props help verify whether the user is allowed to send messages in the given context.\n\n- The Message Composer component also manages the size and layout of the composer, allowing users to comfortably compose and send messages. It provides functionality to retrieve the previous and next messages for reference or navigation purposes.\n\n```\n \n```\n\n- **Note** ComposerContainer is also built from combination multiple components, we will explore components and see how data is sent.\n", - "line": 630 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", - "description": "### The Composer Container\n\n- In the \"ComposerContainer\" component, different composers are rendered based on specific conditions:\n\n- For omnichannel rooms, the component renders the \"ComposerOmnichannel\" composer.\n - If the chat room is a VoIP room, the component renders the \"ComposerVoIP\" composer.\n - In the case of federated rooms, the component renders the \"ComposerFederation\" composer.\n - For anonymous users, the component renders the \"ComposerAnonymous\" composer.\n - If the chat room is read-only, the component renders the \"ComposerReadOnly\" composer.\n - Lastly, if there are any block-related restrictions, such as blocked users or blocking others, the component renders the \"ComposerBlocked\" composer.\n\n **1. Omnichannel**\n ```\n \n ```\n **2. VoIp**\n ```\n \n ```\n **3. Federation**\n ```\n \n ```\n **4. Anonymous Users**\n ```\n \n ```\n **5. Read Only**\n ```\n \n ```\n **6. Composer Blocked**\n ```\n \n ```", - "line": 19 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", - "description": "## ComposerMessage Component\n### And at the end we have ComposerMessage which is responsible for rendering MessageBox(The Text composer at the footer of Channel/Room)", - "line": 79 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", - "description": "## MessageBox Component\n\n- At the bottom of the code, there is a \"MessageBox\" component, which represents the actual chat message box. Its presence is crucial for the proper functioning of the Composer. If you were to comment out or remove the \"MessageBox\" component and run the server, you would observe that the Composer functionality would no longer be available.\n\n- The \"MessageBox\" component plays a pivotal role in enabling users to compose and send messages within the chat interface. It provides the necessary user interface elements, such as input fields and buttons, to facilitate message composition and submission.", - "line": 80 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## The Message Box\n\n#### This file serves as the implementation of the Message Composer component, where you can explore and analyze how the code functions. By examining the code in this file, you can gain a better understanding of its inner workings.\n\n\n#### The Message Composer component receives several props, including *\"rid\"* (room ID), *\"tmid\"* (thread ID), and *\"onSend\"* (handler for sending messages), among others. These props provide necessary data and functionality for the composer to operate effectively.\n\n\n#### By inspecting and experimenting with the code, you can gain insights into how different aspects of the Message Composer are implemented and how they interact with other components and functions.\n\n- The MessageBox is using MessageBoxProps type for defining its type\n```\ntype MessageBoxProps = {rid: IRoom['_id']; ...};\nconst MessageBox = ({rid, tmid, ...}: MessageBoxProps): ReactElement => \n```", - "line": 99 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## Sending Message\n\n#### Rocket chat's Room/channel message composers have multiple options such as uploading files, Writing text messages and Quoted messages, And then we have a send button at the right corner of composer which sends what you have entered.\n\n#### To understand how the send button works for sending a simple text message:\n\n- When the send button is clicked, an event is triggered.\n- The event handler associated with the send button retrieves the text message entered by the user.\n- The text message is then processed and prepared for sending.\n- The necessary information, such as the room/channel ID, sender details, and the text message content, is included.\n- The prepared message is sent to the server via an appropriate network request or function call.\n- The server receives the message and performs further processing, including broadcasting it to the relevant recipients in the room/channel.\nThe message is displayed in the chat interface for all participants to view.", - "line": 345, - "selection": { - "start": { - "line": 3, - "character": 1 - }, - "end": { - "line": 4, - "character": 1 - } - } - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## Message Input \n\n- The MessageComposerInput component plays a crucial role in handling user inputs within the message composer. It is responsible for capturing various types of inputs and encompasses multiple associated actions.\n\n#### The MessageComposerInput component enables users to interact with the message composer through different actions, such as:\n\n1. **Typing and Editing**: Users can enter and edit text within the composer input field.\n\n2. **Formatting**: It may support various formatting options like bold, italic, bullet points, etc., allowing users to apply formatting to their messages.\n\n3. **Mentions**: Users can mention specific individuals or groups within the message by using the appropriate syntax or by selecting them from a list.\n\n4. **Emojis**: It may provide an emoji picker or support emoji shorthand, allowing users to insert emojis into their messages.\n\n5. **Attachments**: Users can attach files or media to their messages, such as images, documents, or videos.", - "line": 380 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "### The Send Button\n\n- To send any message you need to click send button or hit Enter/Return key, when you click send button **handleSendMessage** method/function is called.\n```\n \n```", - "line": 428 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## Handle message send\n\n- This is the function which sends messages further, it Gets text from chat.composer.text where chat is actully using useChat() which is coming from [ChatAPI](./apps/meteor/client/lib/chats/ChatAPI.ts) consisting of multiple functions.\n\n#### The handleSendMessage function is responsible for processing and sending the message to the intended recipients. It performs the following actions:\n\n1. Retrieves the content of the message entered by the user.\n2. Collects additional information needed for sending the message, such as the room/channel ID, sender details, and any associated metadata.\n3. Packages the message data into a suitable format for transmission.\n4. Initiates the process of sending the message, it goes through a WebSocket connection.\n5. The message is then transmitted to the server for further processing.\n6. The server handles the message, ensuring it is delivered to the specified room/channel and received by the intended recipients.\n7. Once the message is successfully sent, it may trigger updates to the chat interface, including displaying the sent message in the conversation history.\n\n- Here we have \n ```\n const chat = useChat(); // useChat is a Context using ChatAPI.\n const text = chat.composer?.text ?? ''; //Text is getting information from useChat Context.\n\n onSend?.({ // onSend was recieved as prop and hence we are returning with some value and a boolean tshow\n\t\t\tvalue: text,\n\t\t\ttshow,\n\t\t});\n ```\n", - "line": 159 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", - "description": "## The onSend Prop\n\n#### In the MessageBox component, the composerProps are passed, and we receive several methods back, including the onSend method. The onSend method takes a value and additional parameters such as tshow, and it returns a promise.\n\n#### Additionally, we utilize the useChat() hook to access various methods available in the ChatAPI. This allows us to perform actions related to chat functionality.\n\n#### By using the onSend method, we can trigger the sending of a message with the provided value. The tshow parameter might be used to display notifications or toasts related to the message sending process. The returned promise can be used to handle any asynchronous operations or to track the status of the message sending.\n\n#### The useChat() hook provides access to methods like sendMessage() or other chat-related functions. These methods can be utilized to perform various actions within the chat interface, such as sending, retrieving, or deleting messages. \n\n```\nawait chat?.action.stop('typing');\n const newMessageSent = await chat?.flows.sendMessage({ // Here the Text is sent to chat.flows.sendMessage \n\t text,\n\t tshow,\n });\n\tif (newMessageSent) onSend?.();\n```", - "line": 27 - }, - { - "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", - "description": "## onSend Function\n\n#### The onSend function sends a new message by calling the chat?.flows.sendMessage() method from useChat Context. It passes the value and tshow parameters as properties of an object and awaits the promise to resolve. The resolved value is stored in the newMessageSent variable.\n\n```\n const newMessageSent = await chat?.flows.sendMessage({ // Here chat.flows.sendMessage() is actually a property comming from ChatAPI. From here we are simply sending value to sendMessage property in ChatAPI\n text,\n tshow,\n\t});\n```\n", - "line": 42 - } - ] +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "2 - How a Message is sent (Client side)", + "steps": [ + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "## How a Message is Sent\n\n### *In this guide, we will explore the protocols, technologies, and tools involved in message sending. By understanding the fundamentals, you will gain confidence in navigating through the repository, Hence you will get more precise understanding of how a message is sent and what are methods being used, how are API calls made and much more. Let's embark on this exciting journey together to uncover the wonders of message sending!*", + "line": 1 + }, + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "## The Chat Room/Channel\n\n- **RoomBody.tsx** is a file that handles the rendering of the Chat Room/Channel on the client side. It brings together multiple components to enable user interaction and communication within the chat environment.\n\n- In this file, there are various methods implemented. Let's specifically examine the process of sending a message through the **MessageComposer** component.\n#### **Additionally, the code in \"RoomBody.tsx\" includes logic for performing various actions related to the chat functionality. These actions might include *sending messages*, *editing* or *deleting* messages, *managing user permissions*, and handling user interactions within the chat environment.**", + "line": 53 + }, + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "### Chat/Room Layout\n\n- The \"RoomBody.tsx\" file is straightforward to comprehend. You can easily navigate through the components and grasp their functionalities.\n\n- The majority of the code in this file revolves around message rendering and handling various chat-related actions.", + "line": 539, + "selection": { + "start": { + "line": 464, + "character": 15 + }, + "end": { + "line": 464, + "character": 19 + } + } + }, + { + "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", + "description": "### Composer Container\n\n- At the bottom of the \"RoomBody.tsx\" file, there is a component responsible for rendering the Message Composer. It accepts several props, including the room ID and subscription details. These props help verify whether the user is allowed to send messages in the given context.\n\n- The Message Composer component also manages the size and layout of the composer, allowing users to comfortably compose and send messages. It provides functionality to retrieve the previous and next messages for reference or navigation purposes.\n\n```\n \n```\n\n- **Note** ComposerContainer is also built from combination multiple components, we will explore components and see how data is sent.\n", + "line": 630 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", + "description": "### The Composer Container\n\n- In the \"ComposerContainer\" component, different composers are rendered based on specific conditions:\n\n- For omnichannel rooms, the component renders the \"ComposerOmnichannel\" composer.\n - If the chat room is a VoIP room, the component renders the \"ComposerVoIP\" composer.\n - In the case of federated rooms, the component renders the \"ComposerFederation\" composer.\n - For anonymous users, the component renders the \"ComposerAnonymous\" composer.\n - If the chat room is read-only, the component renders the \"ComposerReadOnly\" composer.\n - Lastly, if there are any block-related restrictions, such as blocked users or blocking others, the component renders the \"ComposerBlocked\" composer.\n\n **1. Omnichannel**\n ```\n \n ```\n **2. VoIp**\n ```\n \n ```\n **3. Federation**\n ```\n \n ```\n **4. Anonymous Users**\n ```\n \n ```\n **5. Read Only**\n ```\n \n ```\n **6. Composer Blocked**\n ```\n \n ```", + "line": 19 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", + "description": "## ComposerMessage Component\n### And at the end we have ComposerMessage which is responsible for rendering MessageBox(The Text composer at the footer of Channel/Room)", + "line": 79 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", + "description": "## MessageBox Component\n\n- At the bottom of the code, there is a \"MessageBox\" component, which represents the actual chat message box. Its presence is crucial for the proper functioning of the Composer. If you were to comment out or remove the \"MessageBox\" component and run the server, you would observe that the Composer functionality would no longer be available.\n\n- The \"MessageBox\" component plays a pivotal role in enabling users to compose and send messages within the chat interface. It provides the necessary user interface elements, such as input fields and buttons, to facilitate message composition and submission.", + "line": 80 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## The Message Box\n\n#### This file serves as the implementation of the Message Composer component, where you can explore and analyze how the code functions. By examining the code in this file, you can gain a better understanding of its inner workings.\n\n\n#### The Message Composer component receives several props, including *\"rid\"* (room ID), *\"tmid\"* (thread ID), and *\"onSend\"* (handler for sending messages), among others. These props provide necessary data and functionality for the composer to operate effectively.\n\n\n#### By inspecting and experimenting with the code, you can gain insights into how different aspects of the Message Composer are implemented and how they interact with other components and functions.\n\n- The MessageBox is using MessageBoxProps type for defining its type\n```\ntype MessageBoxProps = {rid: IRoom['_id']; ...};\nconst MessageBox = ({rid, tmid, ...}: MessageBoxProps): ReactElement => \n```", + "line": 99 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## Sending Message\n\n#### Rocket chat's Room/channel message composers have multiple options such as uploading files, Writing text messages and Quoted messages, And then we have a send button at the right corner of composer which sends what you have entered.\n\n#### To understand how the send button works for sending a simple text message:\n\n- When the send button is clicked, an event is triggered.\n- The event handler associated with the send button retrieves the text message entered by the user.\n- The text message is then processed and prepared for sending.\n- The necessary information, such as the room/channel ID, sender details, and the text message content, is included.\n- The prepared message is sent to the server via an appropriate network request or function call.\n- The server receives the message and performs further processing, including broadcasting it to the relevant recipients in the room/channel.\nThe message is displayed in the chat interface for all participants to view.", + "line": 345, + "selection": { + "start": { + "line": 3, + "character": 1 + }, + "end": { + "line": 4, + "character": 1 + } + } + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## Message Input \n\n- The MessageComposerInput component plays a crucial role in handling user inputs within the message composer. It is responsible for capturing various types of inputs and encompasses multiple associated actions.\n\n#### The MessageComposerInput component enables users to interact with the message composer through different actions, such as:\n\n1. **Typing and Editing**: Users can enter and edit text within the composer input field.\n\n2. **Formatting**: It may support various formatting options like bold, italic, bullet points, etc., allowing users to apply formatting to their messages.\n\n3. **Mentions**: Users can mention specific individuals or groups within the message by using the appropriate syntax or by selecting them from a list.\n\n4. **Emojis**: It may provide an emoji picker or support emoji shorthand, allowing users to insert emojis into their messages.\n\n5. **Attachments**: Users can attach files or media to their messages, such as images, documents, or videos.", + "line": 380 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "### The Send Button\n\n- To send any message you need to click send button or hit Enter/Return key, when you click send button **handleSendMessage** method/function is called.\n```\n \n```", + "line": 428 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", + "description": "## handleSendMessage Function\n\n- This is the function which sends messages further, it Gets text from chat.composer.text where chat is actully using useChat() which is coming from [ChatAPI](./apps/meteor/client/lib/chats/ChatAPI.ts) consisting of multiple functions.\n\n#### The handleSendMessage function is responsible for processing and sending the message to the intended recipients. It performs the following actions:\n\n1. Retrieves the content of the message entered by the user.\n2. Collects additional information needed for sending the message, such as the room/channel ID, sender details, and any associated metadata.\n3. Packages the message data into a suitable format for transmission.\n4. Initiates the process of sending the message, it goes through a WebSocket connection.\n5. The message is then transmitted to the server for further processing.\n6. The server handles the message, ensuring it is delivered to the specified room/channel and received by the intended recipients.\n7. Once the message is successfully sent, it may trigger updates to the chat interface, including displaying the sent message in the conversation history.\n\n- Here we have \n ```\n const chat = useChat(); // useChat is a Context using ChatAPI.\n const text = chat.composer?.text ?? ''; //Text is getting information from useChat Context.\n\n onSend?.({ // onSend was recieved as prop and hence we are returning with some value and a boolean tshow\n\t\t\tvalue: text,\n\t\t\ttshow,\n\t\t});\n ```\n", + "line": 159 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", + "description": "## The onSend Prop\n\n#### In the MessageBox component, the composerProps are passed, and we receive several methods back, including the onSend method. The onSend method takes a value and additional parameters such as tshow, and it returns a promise.\n\n#### Additionally, we utilize the useChat() hook to access various methods available in the ChatAPI. This allows us to perform actions related to chat functionality.\n\n#### By using the onSend method, we can trigger the sending of a message with the provided value. The tshow parameter might be used to display notifications or toasts related to the message sending process. The returned promise can be used to handle any asynchronous operations or to track the status of the message sending.\n\n#### The useChat() hook provides access to methods like sendMessage() or other chat-related functions. These methods can be utilized to perform various actions within the chat interface, such as sending, retrieving, or deleting messages. \n\n```\nawait chat?.action.stop('typing');\n const newMessageSent = await chat?.flows.sendMessage({ // Here the Text is sent to chat.flows.sendMessage \n\t text,\n\t tshow,\n });\n\tif (newMessageSent) onSend?.();\n```", + "line": 27 + }, + { + "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", + "description": "## onSend Function\n\n#### The onSend function sends a new message by calling the chat?.flows.sendMessage() method from useChat Context. It passes the value and tshow parameters as properties of an object and awaits the promise to resolve. The resolved value is stored in the newMessageSent variable.\n\n```\n const newMessageSent = await chat?.flows.sendMessage({ // Here chat.flows.sendMessage() is actually a property coming from ChatAPI. From here we are simply sending value to sendMessage property in ChatAPI\n text,\n tshow,\n\t});\n```\n", + "line": 42 + } + ] } \ No newline at end of file diff --git a/.tours/4---how-to-create-an-endpoint.tour b/.tours/4---how-to-create-an-endpoint.tour index f6bfe6611fca..9bf869e5ce93 100644 --- a/.tours/4---how-to-create-an-endpoint.tour +++ b/.tours/4---how-to-create-an-endpoint.tour @@ -40,7 +40,7 @@ { "file": "apps/meteor/app/api/server/v1/chat.ts", "description": "## Calling REST endpoint\n\n### Let us try to call an REST Endpoint for sending message\n- In order to make Api call you need Personal Access Tokens for user authentication. **[Get Token here](https://docs.rocket.chat/use-rocket.chat/user-guides/user-panel/my-account#personal-access-tokens)** (Remember get access token from your own local server)\n\n- To get User-Id go to admin settings then users(or http://localhost:3000/admin/users) select your account and copy unique user id from URL (http://localhost:3000/admin/users/info/u3y2jXw5ayckPciE9) here we have **u3y2jXw5ayckPciE9** as user id, Try finding yours\n\n- After you get you Personal access token Start Rocket Chat server on your local machine and in another terminal enter this command.\n\n```\ncurl -H \"X-Auth-Token: ENTER_YOUR_TOKEN \" \\\n -H \"X-User-Id: ENTER_YOUR_USERID \" \\\n -H \"Content-type:application/json\" \\\n http://localhost:3000/api/v1/chat.sendMessage \\\n -d '{\"message\":{\"rid\":\"GENERAL\", \"msg\":\"Hello From Rocket Chat\"}}'\n```\n- You will get a success response once your request is successful and vice versa\n\n### Similarly you can also create your own REST Endpoint with unique route name and define what it needs to do by adding different funtionalities in it. ", - "line": 239, + "line": 211, "selection": { "start": { "line": 6, diff --git a/.tours/5---how-to-create-a-db-model.tour b/.tours/5---how-to-create-a-db-model.tour index d52f46518177..688f8350e8f9 100644 --- a/.tours/5---how-to-create-a-db-model.tour +++ b/.tours/5---how-to-create-a-db-model.tour @@ -28,7 +28,7 @@ }, { "file": "apps/meteor/server/models/Messages.ts", - "description": "## Registering\n\n### Here we are using the registerModel import and passing 'IMessageModel' and using class MessagesRaw we pass in db and trashCollection\n\n### The register model funtion looks something like this -\n```\nfunction registerModel>(name: string, instance: TModel | (() => TModel)): void;\n```\n\n### And we pass data into it like - \n```\n registerModel('IMessagesModel', new MessagesRaw(db, trashCollection));\n //It becomes something like this, Here IMessageModel is basically implemented in MessagesRaw as we saw in previous steps\n registerModel(name: string, instance: MessagesRaw | (() => MessagesRaw)): void\n```", + "description": "## Registering\n\n### Here we are using the registerModel import and passing 'IMessageModel' and using class MessagesRaw we pass in db and trashCollection\n\n### The register model function looks something like this -\n```\nfunction registerModel>(name: string, instance: TModel | (() => TModel)): void;\n```\n\n### And we pass data into it like - \n```\n registerModel('IMessagesModel', new MessagesRaw(db, trashCollection));\n //It becomes something like this, Here IMessageModel is basically implemented in MessagesRaw as we saw in previous steps\n registerModel(name: string, instance: MessagesRaw | (() => MessagesRaw)): void\n```", "line": 7 } ] diff --git a/.tours/6---how-to-use-a-db-model.tour b/.tours/6---how-to-use-a-db-model.tour new file mode 100644 index 000000000000..1cbc1c981553 --- /dev/null +++ b/.tours/6---how-to-use-a-db-model.tour @@ -0,0 +1,40 @@ +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "6 - How to use a DB model", + "steps": [ + { + "directory": "apps/meteor/server/methods", + "description": "## How to use a DB model\n\n### We discussed about how to create a DB model in previous tour, Here we would be learning how to use a DB model\n\n### Let us Learn how to use DB model by taking an example of loading older messages in channels, This is an meteor method, which is called when we scroll up and try to load older messages (/api/v1/method.call/loadHistory)" + }, + { + "file": "apps/meteor/server/methods/loadHistory.ts", + "description": "## loadHistory Endpoint\n\n### Here We hava an meteor endpoint which takes rid, end, limit, ls, showThreadMessage as argument, This method is responsible for rendering older messages \n\n- You can go through the methods used below they are quiet easy to understand", + "line": 32 + }, + { + "file": "apps/meteor/server/methods/loadHistory.ts", + "description": "## The LoadMessageHistory \n\n### LoadMessageHistory function is called here and it takes userId, rid, end, limit, ls, showThreadMessages", + "line": 64 + }, + { + "file": "apps/meteor/app/lib/server/functions/loadMessageHistory.ts", + "description": "## loadMessageHistory function\n\n### Here we import Messages and Rooms DB models from @rocket.chat/models and we will furhter use them in our function\n\n", + "line": 2 + }, + { + "file": "apps/meteor/app/lib/server/functions/loadMessageHistory.ts", + "description": "## Explanation\n\n- The loadMessageHistory function is responsible for retrieving a history of messages from a specific room in Rocket.Chat. It accepts several parameters including the user ID, room ID, end timestamp, limit, last seen timestamp, whether to show thread messages, and offset.\n\n- First, it fetches the room using the provided room ID and checks if it exists. If the room doesn't exist, an error is thrown.\n\n- Next, it determines the types of hidden system messages for the room.\n\n- Then, it defines options for querying the messages, including sorting by timestamp in descending order, applying the limit and offset.", + "line": 8 + }, + { + "file": "apps/meteor/app/lib/server/functions/loadMessageHistory.ts", + "description": "## Explanation 2\n\n- Based on the provided end timestamp, it retrieves visible messages before that timestamp, excluding the hidden message types. If no end timestamp is provided, it retrieves all visible messages in the room, excluding hidden message types.\n\n- The retrieved records are then normalized and processed for the specific user, taking into account any restrictions or modifications based on the user's permissions or settings.\n\n- If a last seen timestamp is provided, it checks if there are unread messages after that timestamp. If there are unread messages, it retrieves the first unread message and calculates the total count of unread messages that are not loaded yet.\n\n- Finally, it returns an object containing the retrieved messages, the first unread message, and the count of unread messages that are not loaded.", + "line": 42 + }, + { + "file": "apps/meteor/app/lib/server/functions/loadMessageHistory.ts", + "description": "## Returns\n\n### In the end it returns messages, firstUnread, unreadNotLoaded you can go through the code above its easy to understand", + "line": 88 + } + ] +} \ No newline at end of file From 33be8f16b9332ca6818a366cdf4c884ed4879e9e Mon Sep 17 00:00:00 2001 From: Tasso Evangelista Date: Tue, 27 Jun 2023 10:54:02 -0300 Subject: [PATCH 015/149] fix(livechat): Storybook adjustments and TypeScript migration (#29631) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .changeset/strong-trains-enjoy.md | 5 + .../preact-npm-10.15.1-bd458de913.patch | 13 + ...nichannel-auto-onhold-chat-closing.spec.ts | 6 +- ...nnel-auto-transfer-unanswered-chat.spec.ts | 6 +- ...nichannel-canned-responses-sidebar.spec.ts | 6 +- ...nel-changing-room-priority-and-sla.spec.ts | 6 +- .../omnichannel-chat-history.spec.ts | 8 +- .../omnichannel-close-chat.spec.ts | 6 +- .../omnichannel-close-inquiry.spec.ts | 14 +- .../omnichannel-contact-info.spec.ts | 6 +- .../omnichannel/omnichannel-livechat.spec.ts | 6 +- .../omnichannel-send-transcript.spec.ts | 6 +- .../omnichannel/omnichannel-takeChat.spec.ts | 6 +- ...channel-transfer-to-another-agents.spec.ts | 6 +- .../omnichannel/omnichannel-triggers.spec.ts | 6 +- .../e2e/page-objects/omnichannel-livechat.ts | 9 +- package.json | 3 +- packages/livechat/.storybook/main.js | 3 +- packages/livechat/babel.config.js | 31 +- packages/livechat/package.json | 15 +- .../livechat/src/components/Alert/index.tsx | 3 +- .../livechat/src/components/Alert/stories.js | 82 ---- .../livechat/src/components/Alert/stories.tsx | 66 +++ .../livechat/src/components/Avatar/stories.js | 92 ---- .../src/components/Avatar/stories.tsx | 66 +++ .../livechat/src/components/Button/index.tsx | 9 +- .../livechat/src/components/Button/stories.js | 211 --------- .../src/components/Button/stories.tsx | 112 +++++ .../src/components/ButtonGroup/stories.js | 50 --- .../src/components/ButtonGroup/stories.tsx | 62 +++ .../src/components/Composer/stories.js | 89 ---- .../src/components/Composer/stories.tsx | 77 ++++ .../src/components/FilesDropTarget/stories.js | 103 ----- .../components/FilesDropTarget/stories.tsx | 91 ++++ .../livechat/src/components/Footer/index.js | 2 +- .../livechat/src/components/Footer/stories.js | 53 --- .../src/components/Footer/stories.tsx | 65 +++ .../src/components/Form/DateInput/stories.js | 87 ---- .../src/components/Form/DateInput/stories.tsx | 59 +++ .../src/components/Form/FormField/index.js | 2 +- .../src/components/Form/FormField/stories.js | 46 -- .../src/components/Form/FormField/stories.tsx | 39 ++ .../components/Form/PasswordInput/stories.js | 86 ---- .../components/Form/PasswordInput/stories.tsx | 59 +++ .../components/Form/SelectInput/stories.js | 111 ----- .../components/Form/SelectInput/stories.tsx | 64 +++ .../src/components/Form/TextInput/stories.js | 107 ----- .../src/components/Form/TextInput/stories.tsx | 67 +++ .../livechat/src/components/Form/stories.js | 43 -- .../livechat/src/components/Form/stories.tsx | 51 +++ .../livechat/src/components/Header/index.js | 16 +- .../livechat/src/components/Header/stories.js | 254 ----------- .../src/components/Header/stories.tsx | 222 ++++++++++ .../src/components/Menu/Group.stories.tsx | 48 ++ .../src/components/Menu/Item.stories.tsx | 92 ++++ .../src/components/Menu/Menu.stories.tsx | 62 +++ .../components/Menu/PopoverMenu.stories.tsx | 45 ++ .../livechat/src/components/Menu/index.js | 17 +- .../livechat/src/components/Menu/stories.js | 164 ------- .../Messages/AudioAttachment/stories.js | 11 - .../Messages/AudioAttachment/stories.tsx | 19 + .../Messages/FileAttachment/stories.js | 20 - .../Messages/FileAttachment/stories.tsx | 62 +++ .../Messages/ImageAttachment/stories.js | 11 - .../Messages/ImageAttachment/stories.tsx | 19 + .../Message/{stories.js => stories.tsx} | 63 +-- .../Messages/MessageAvatars/stories.js | 23 - .../Messages/MessageAvatars/stories.tsx | 46 ++ .../Messages/MessageBlocks/index.js | 2 +- .../MessageBlocks/{stories.js => stories.tsx} | 10 +- .../Messages/MessageBubble/stories.js | 31 -- .../Messages/MessageBubble/stories.tsx | 44 ++ .../Messages/MessageList/stories.js | 107 ----- .../Messages/MessageList/stories.tsx | 99 +++++ .../Messages/MessageSeparator/stories.js | 14 - .../Messages/MessageSeparator/stories.tsx | 33 ++ .../Messages/MessageTime/stories.js | 14 - .../Messages/MessageTime/stories.tsx | 29 ++ .../components/Messages/TypingDots/stories.js | 10 - .../Messages/TypingDots/stories.tsx | 20 + .../Messages/TypingIndicator/stories.js | 19 - .../Messages/TypingIndicator/stories.tsx | 29 ++ .../Messages/VideoAttachment/stories.js | 11 - .../Messages/VideoAttachment/stories.tsx | 21 + .../Messages/{constants.js => constants.ts} | 0 .../livechat/src/components/Modal/stories.js | 75 ---- .../livechat/src/components/Modal/stories.tsx | 83 ++++ .../livechat/src/components/Popover/index.js | 1 + .../src/components/Popover/stories.js | 36 -- .../src/components/Popover/stories.tsx | 44 ++ .../src/components/Screen/Footer.stories.tsx | 67 +++ .../livechat/src/components/Screen/index.js | 1 + .../livechat/src/components/Screen/stories.js | 321 -------------- .../src/components/Screen/stories.tsx | 121 +++++ .../livechat/src/components/Sound/stories.js | 18 - .../livechat/src/components/Sound/stories.tsx | 33 ++ .../src/components/Tooltip/stories.js | 44 -- .../src/components/Tooltip/stories.tsx | 59 +++ ...ns.stories.js => ActionsBlock.stories.tsx} | 5 +- .../ButtonElement/{stories.js => stories.tsx} | 44 +- ...xt.stories.js => ContextBlock.stories.tsx} | 6 +- ...er.stories.js => DividerBlock.stories.tsx} | 4 +- ...mage.stories.js => ImageBlock.stories.tsx} | 6 +- ...on.stories.js => SectionBlock.stories.tsx} | 7 +- .../uiKit/message/{index.js => index.tsx} | 26 +- packages/livechat/src/global.d.ts | 33 +- packages/livechat/src/helpers.stories.js | 29 +- packages/livechat/src/icons/stories.js | 52 --- packages/livechat/src/icons/stories.tsx | 57 +++ packages/livechat/src/routes/Chat/stories.js | 131 ------ packages/livechat/src/routes/Chat/stories.tsx | 91 ++++ .../src/routes/ChatFinished/stories.js | 31 -- .../src/routes/ChatFinished/stories.tsx | 34 ++ .../src/routes/GDPRAgreement/stories.js | 19 - .../src/routes/GDPRAgreement/stories.tsx | 27 ++ .../src/routes/LeaveMessage/stories.js | 43 -- .../src/routes/LeaveMessage/stories.tsx | 42 ++ .../livechat/src/routes/Register/stories.js | 106 ----- .../livechat/src/routes/Register/stories.tsx | 71 +++ .../src/routes/SwitchDepartment/stories.js | 63 --- .../src/routes/SwitchDepartment/stories.tsx | 48 ++ .../src/routes/TriggerMessage/stories.js | 56 --- .../src/routes/TriggerMessage/stories.tsx | 48 ++ yarn.lock | 417 ++++++------------ 124 files changed, 2972 insertions(+), 3335 deletions(-) create mode 100644 .changeset/strong-trains-enjoy.md create mode 100644 .yarn/patches/preact-npm-10.15.1-bd458de913.patch delete mode 100644 packages/livechat/src/components/Alert/stories.js create mode 100644 packages/livechat/src/components/Alert/stories.tsx delete mode 100644 packages/livechat/src/components/Avatar/stories.js create mode 100644 packages/livechat/src/components/Avatar/stories.tsx delete mode 100644 packages/livechat/src/components/Button/stories.js create mode 100644 packages/livechat/src/components/Button/stories.tsx delete mode 100644 packages/livechat/src/components/ButtonGroup/stories.js create mode 100644 packages/livechat/src/components/ButtonGroup/stories.tsx delete mode 100644 packages/livechat/src/components/Composer/stories.js create mode 100644 packages/livechat/src/components/Composer/stories.tsx delete mode 100644 packages/livechat/src/components/FilesDropTarget/stories.js create mode 100644 packages/livechat/src/components/FilesDropTarget/stories.tsx delete mode 100644 packages/livechat/src/components/Footer/stories.js create mode 100644 packages/livechat/src/components/Footer/stories.tsx delete mode 100644 packages/livechat/src/components/Form/DateInput/stories.js create mode 100644 packages/livechat/src/components/Form/DateInput/stories.tsx delete mode 100644 packages/livechat/src/components/Form/FormField/stories.js create mode 100644 packages/livechat/src/components/Form/FormField/stories.tsx delete mode 100644 packages/livechat/src/components/Form/PasswordInput/stories.js create mode 100644 packages/livechat/src/components/Form/PasswordInput/stories.tsx delete mode 100644 packages/livechat/src/components/Form/SelectInput/stories.js create mode 100644 packages/livechat/src/components/Form/SelectInput/stories.tsx delete mode 100644 packages/livechat/src/components/Form/TextInput/stories.js create mode 100644 packages/livechat/src/components/Form/TextInput/stories.tsx delete mode 100644 packages/livechat/src/components/Form/stories.js create mode 100644 packages/livechat/src/components/Form/stories.tsx delete mode 100644 packages/livechat/src/components/Header/stories.js create mode 100644 packages/livechat/src/components/Header/stories.tsx create mode 100644 packages/livechat/src/components/Menu/Group.stories.tsx create mode 100644 packages/livechat/src/components/Menu/Item.stories.tsx create mode 100644 packages/livechat/src/components/Menu/Menu.stories.tsx create mode 100644 packages/livechat/src/components/Menu/PopoverMenu.stories.tsx delete mode 100644 packages/livechat/src/components/Menu/stories.js delete mode 100644 packages/livechat/src/components/Messages/AudioAttachment/stories.js create mode 100644 packages/livechat/src/components/Messages/AudioAttachment/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/FileAttachment/stories.js create mode 100644 packages/livechat/src/components/Messages/FileAttachment/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/ImageAttachment/stories.js create mode 100644 packages/livechat/src/components/Messages/ImageAttachment/stories.tsx rename packages/livechat/src/components/Messages/Message/{stories.js => stories.tsx} (79%) delete mode 100644 packages/livechat/src/components/Messages/MessageAvatars/stories.js create mode 100644 packages/livechat/src/components/Messages/MessageAvatars/stories.tsx rename packages/livechat/src/components/Messages/MessageBlocks/{stories.js => stories.tsx} (96%) delete mode 100644 packages/livechat/src/components/Messages/MessageBubble/stories.js create mode 100644 packages/livechat/src/components/Messages/MessageBubble/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/MessageList/stories.js create mode 100644 packages/livechat/src/components/Messages/MessageList/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/MessageSeparator/stories.js create mode 100644 packages/livechat/src/components/Messages/MessageSeparator/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/MessageTime/stories.js create mode 100644 packages/livechat/src/components/Messages/MessageTime/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/TypingDots/stories.js create mode 100644 packages/livechat/src/components/Messages/TypingDots/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/TypingIndicator/stories.js create mode 100644 packages/livechat/src/components/Messages/TypingIndicator/stories.tsx delete mode 100644 packages/livechat/src/components/Messages/VideoAttachment/stories.js create mode 100644 packages/livechat/src/components/Messages/VideoAttachment/stories.tsx rename packages/livechat/src/components/Messages/{constants.js => constants.ts} (100%) delete mode 100644 packages/livechat/src/components/Modal/stories.js create mode 100644 packages/livechat/src/components/Modal/stories.tsx delete mode 100644 packages/livechat/src/components/Popover/stories.js create mode 100644 packages/livechat/src/components/Popover/stories.tsx create mode 100644 packages/livechat/src/components/Screen/Footer.stories.tsx delete mode 100644 packages/livechat/src/components/Screen/stories.js create mode 100644 packages/livechat/src/components/Screen/stories.tsx delete mode 100644 packages/livechat/src/components/Sound/stories.js create mode 100644 packages/livechat/src/components/Sound/stories.tsx delete mode 100644 packages/livechat/src/components/Tooltip/stories.js create mode 100644 packages/livechat/src/components/Tooltip/stories.tsx rename packages/livechat/src/components/uiKit/message/{actions.stories.js => ActionsBlock.stories.tsx} (97%) rename packages/livechat/src/components/uiKit/message/ButtonElement/{stories.js => stories.tsx} (75%) rename packages/livechat/src/components/uiKit/message/{context.stories.js => ContextBlock.stories.tsx} (92%) rename packages/livechat/src/components/uiKit/message/{divider.stories.js => DividerBlock.stories.tsx} (85%) rename packages/livechat/src/components/uiKit/message/{image.stories.js => ImageBlock.stories.tsx} (85%) rename packages/livechat/src/components/uiKit/message/{section.stories.js => SectionBlock.stories.tsx} (96%) rename packages/livechat/src/components/uiKit/message/{index.js => index.tsx} (75%) delete mode 100644 packages/livechat/src/icons/stories.js create mode 100644 packages/livechat/src/icons/stories.tsx delete mode 100644 packages/livechat/src/routes/Chat/stories.js create mode 100644 packages/livechat/src/routes/Chat/stories.tsx delete mode 100644 packages/livechat/src/routes/ChatFinished/stories.js create mode 100644 packages/livechat/src/routes/ChatFinished/stories.tsx delete mode 100644 packages/livechat/src/routes/GDPRAgreement/stories.js create mode 100644 packages/livechat/src/routes/GDPRAgreement/stories.tsx delete mode 100644 packages/livechat/src/routes/LeaveMessage/stories.js create mode 100644 packages/livechat/src/routes/LeaveMessage/stories.tsx delete mode 100644 packages/livechat/src/routes/Register/stories.js create mode 100644 packages/livechat/src/routes/Register/stories.tsx delete mode 100644 packages/livechat/src/routes/SwitchDepartment/stories.js create mode 100644 packages/livechat/src/routes/SwitchDepartment/stories.tsx delete mode 100644 packages/livechat/src/routes/TriggerMessage/stories.js create mode 100644 packages/livechat/src/routes/TriggerMessage/stories.tsx diff --git a/.changeset/strong-trains-enjoy.md b/.changeset/strong-trains-enjoy.md new file mode 100644 index 000000000000..91aa7f417cb1 --- /dev/null +++ b/.changeset/strong-trains-enjoy.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/livechat': patch +--- + +Storybook adjustments, TypeScript migration, and minor fixes diff --git a/.yarn/patches/preact-npm-10.15.1-bd458de913.patch b/.yarn/patches/preact-npm-10.15.1-bd458de913.patch new file mode 100644 index 000000000000..b7e38e6e3b82 --- /dev/null +++ b/.yarn/patches/preact-npm-10.15.1-bd458de913.patch @@ -0,0 +1,13 @@ +diff --git a/src/index.d.ts b/src/index.d.ts +index c57fcb6f6f3734d182313c324de9b28e6dcee68d..b7602c71e9f4b4254f4056fb598ef72ab157c921 100644 +--- a/src/index.d.ts ++++ b/src/index.d.ts +@@ -110,7 +110,7 @@ export interface ComponentConstructor

+ // Type alias for a component instance considered generally, whether stateless or stateful. + export type AnyComponent

= + | FunctionComponent

+- | Component; ++ | ComponentConstructor; + + export interface Component

{ + componentWillMount?(): void; diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts index a20ccf05006a..e3ed090689c5 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts @@ -36,7 +36,7 @@ test.describe('omnichannel-auto-onhold-chat-closing', () => { await agent.page.close(); }); - test.beforeEach(async ({ page }) => { + test.beforeEach(async ({ page, api }) => { // make "user-1" online await agent.poHomeChannel.sidenav.switchStatus('online'); @@ -45,9 +45,9 @@ test.describe('omnichannel-auto-onhold-chat-closing', () => { name: faker.person.firstName(), email: faker.internet.email(), }; - poLiveChat = new OmnichannelLiveChat(page); + poLiveChat = new OmnichannelLiveChat(page, api); await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newVisitor, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-transfer-unanswered-chat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-transfer-unanswered-chat.spec.ts index 67bef3dbac52..a1620f3d70c7 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-transfer-unanswered-chat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-transfer-unanswered-chat.spec.ts @@ -42,7 +42,7 @@ test.describe('omnichannel-auto-transfer-unanswered-chat', () => { await agent2.page.close(); }); - test.beforeEach(async ({ page }) => { + test.beforeEach(async ({ page, api }) => { // make "user-1" online await agent1.poHomeChannel.sidenav.switchOmnichannelStatus('online'); await agent2.poHomeChannel.sidenav.switchOmnichannelStatus('offline'); @@ -52,9 +52,9 @@ test.describe('omnichannel-auto-transfer-unanswered-chat', () => { name: faker.person.firstName(), email: faker.internet.email(), }; - poLiveChat = new OmnichannelLiveChat(page); + poLiveChat = new OmnichannelLiveChat(page, api); await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newVisitor, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-canned-responses-sidebar.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-canned-responses-sidebar.spec.ts index 4df5ee1dc8d1..b0c7c22491b6 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-canned-responses-sidebar.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-canned-responses-sidebar.spec.ts @@ -28,8 +28,8 @@ test.describe('Omnichannel Canned Responses Sidebar', () => { const { page } = await createAuxContext(browser, Users.user1); agent = { page, poHomeChannel: new HomeChannel(page) }; }); - test.beforeEach(async ({ page }) => { - poLiveChat = new OmnichannelLiveChat(page); + test.beforeEach(async ({ page, api }) => { + poLiveChat = new OmnichannelLiveChat(page, api); }); test.afterAll(async ({ api }) => { @@ -41,7 +41,7 @@ test.describe('Omnichannel Canned Responses Sidebar', () => { test('Receiving a message from visitor', async ({ page }) => { await test.step('Expect send a message as a visitor', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newUser, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts index 042e43c5cc5e..d4cc6484975e 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts @@ -57,14 +57,14 @@ test.describe('omnichannel-changing-room-priority-and-sla', () => { await agent.page.close(); }); - test('expect to initiate a new livechat conversation', async ({ page }) => { + test('expect to initiate a new livechat conversation', async ({ page, api }) => { newVisitor = { name: faker.person.firstName(), email: faker.internet.email(), }; - poLiveChat = new OmnichannelLiveChat(page); + poLiveChat = new OmnichannelLiveChat(page, api); await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newVisitor, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-chat-history.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-chat-history.spec.ts index 303285db3523..044d3df516f0 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-chat-history.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-chat-history.spec.ts @@ -24,8 +24,8 @@ test.describe('Omnichannel chat histr', () => { const { page } = await createAuxContext(browser, Users.user1); agent = { page, poHomeOmnichannel: new HomeOmnichannel(page) }; }); - test.beforeEach(async ({ page }) => { - poLiveChat = new OmnichannelLiveChat(page); + test.beforeEach(async ({ page, api }) => { + poLiveChat = new OmnichannelLiveChat(page, api); }); test.afterAll(async ({ api }) => { @@ -37,7 +37,7 @@ test.describe('Omnichannel chat histr', () => { test('Receiving a message from visitor', async ({ page }) => { await test.step('Expect send a message as a visitor', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newUser, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); @@ -56,7 +56,7 @@ test.describe('Omnichannel chat histr', () => { await test.step('Expect send a message as a visitor again to reopen chat', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); }); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-close-chat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-close-chat.spec.ts index a69ccb1751f7..75fb398ee1de 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-close-chat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-close-chat.spec.ts @@ -25,8 +25,8 @@ test.describe('Omnichannel close chat', () => { const { page } = await createAuxContext(browser, Users.user1); agent = { page, poHomeOmnichannel: new HomeOmnichannel(page) }; }); - test.beforeEach(async ({ page }) => { - poLiveChat = new OmnichannelLiveChat(page); + test.beforeEach(async ({ page, api }) => { + poLiveChat = new OmnichannelLiveChat(page, api); }); test.afterAll(async ({ api }) => { @@ -38,7 +38,7 @@ test.describe('Omnichannel close chat', () => { test('Receiving a message from visitor', async ({ page }) => { await test.step('Expect send a message as a visitor', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newUser, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts index f61da3132998..85c523c20663 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-close-inquiry.spec.ts @@ -18,17 +18,15 @@ test.describe('Omnichannel close inquiry', () => { email: faker.internet.email(), }; - await Promise.all([ - await api.post('/livechat/users/manager', { username: 'user1' }), - await api.post('/livechat/users/agent', { username: 'user1' }), - await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }).then((res) => expect(res.status()).toBe(200)), - ]); + await api.post('/livechat/users/manager', { username: 'user1' }); + await api.post('/livechat/users/agent', { username: 'user1' }); + await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }).then((res) => expect(res.status()).toBe(200)); const { page } = await createAuxContext(browser, Users.user1); agent = { page, poHomeOmnichannel: new HomeOmnichannel(page) }; }); - test.beforeEach(async ({ page }) => { - poLiveChat = new OmnichannelLiveChat(page); + test.beforeEach(async ({ page, api }) => { + poLiveChat = new OmnichannelLiveChat(page, api); }); test.afterAll(async ({ api }) => { @@ -43,7 +41,7 @@ test.describe('Omnichannel close inquiry', () => { test('Receiving a message from visitor', async ({ page }) => { await test.step('Expect send a message as a visitor', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newUser, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-info.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-info.spec.ts index 6f3daaffe964..266df1849292 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-info.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-info.spec.ts @@ -25,8 +25,8 @@ test.describe('Omnichannel contact info', () => { const { page } = await createAuxContext(browser, Users.user1); agent = { page, poHomeChannel: new HomeChannel(page) }; }); - test.beforeEach(async ({ page }) => { - poLiveChat = new OmnichannelLiveChat(page); + test.beforeEach(async ({ page, api }) => { + poLiveChat = new OmnichannelLiveChat(page, api); }); test.afterAll(async ({ api }) => { @@ -38,7 +38,7 @@ test.describe('Omnichannel contact info', () => { test('Receiving a message from visitor, and seeing its information', async ({ page }) => { await test.step('Expect send a message as a visitor', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newUser, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts index 77ab9cf8d635..c5250b1d1150 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat.spec.ts @@ -21,7 +21,7 @@ test.describe('Livechat', () => { await expect(statusCode).toBe(200); page = await browser.newPage(); - poLiveChat = new OmnichannelLiveChat(page); + poLiveChat = new OmnichannelLiveChat(page, api); const { page: pageCtx } = await createAuxContext(browser, Users.user1); poAuxContext = { page: pageCtx, poHomeOmnichannel: new HomeOmnichannel(pageCtx) }; @@ -36,7 +36,7 @@ test.describe('Livechat', () => { test('Send message to online agent', async () => { await test.step('Expect message to be sent by livechat', async () => { - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newUser, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user'); @@ -59,7 +59,7 @@ test.describe('Livechat', () => { }); await test.step('Expect when user minimizes the livechat screen, the composer should be hidden', async () => { - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await expect(page.locator('[contenteditable="true"]')).not.toBeVisible(); }); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-send-transcript.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-send-transcript.spec.ts index a1c15e44df94..032be52d1472 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-send-transcript.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-send-transcript.spec.ts @@ -25,8 +25,8 @@ test.describe('omnichannel-transcript', () => { const { page } = await createAuxContext(browser, Users.user1); agent = { page, poHomeChannel: new HomeChannel(page) }; }); - test.beforeEach(async ({ page }) => { - poLiveChat = new OmnichannelLiveChat(page); + test.beforeEach(async ({ page, api }) => { + poLiveChat = new OmnichannelLiveChat(page, api); }); test.afterAll(async ({ api }) => { @@ -38,7 +38,7 @@ test.describe('omnichannel-transcript', () => { test('Receiving a message from visitor', async ({ page }) => { await test.step('Expect send a message as a visitor', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newUser, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts index 6314ee395f45..4738158ca23c 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts @@ -33,7 +33,7 @@ test.describe('omnichannel-takeChat', () => { await agent.page.close(); }); - test.beforeEach(async ({ page }) => { + test.beforeEach(async ({ page, api }) => { // make "user-1" online await agent.poHomeChannel.sidenav.switchStatus('online'); @@ -42,9 +42,9 @@ test.describe('omnichannel-takeChat', () => { name: `${faker.person.firstName()} ${faker.string.uuid()}`, email: faker.internet.email(), }; - poLiveChat = new OmnichannelLiveChat(page); + poLiveChat = new OmnichannelLiveChat(page, api); await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newVisitor, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts index 1a968253deff..3c74065a9a84 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts @@ -38,7 +38,7 @@ test.describe('omnichannel-transfer-to-another-agent', () => { await agent1.page.close(); await agent2.page.close(); }); - test.beforeEach(async ({ page }) => { + test.beforeEach(async ({ page, api }) => { // make "user-1" online & "user-2" offline so that chat can be automatically routed to "user-1" await agent1.poHomeOmnichannel.sidenav.switchStatus('online'); await agent2.poHomeOmnichannel.sidenav.switchStatus('offline'); @@ -48,9 +48,9 @@ test.describe('omnichannel-transfer-to-another-agent', () => { name: faker.person.firstName(), email: faker.internet.email(), }; - poLiveChat = new OmnichannelLiveChat(page); + poLiveChat = new OmnichannelLiveChat(page, api); await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); await poLiveChat.sendMessage(newVisitor, false); await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); await poLiveChat.btnSendMessageToOnlineAgent.click(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts index f1a863d8fc9d..d882296fae44 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-triggers.spec.ts @@ -32,8 +32,8 @@ test.describe.serial('omnichannel-triggers', () => { await page.locator('.main-content').waitFor(); }); - test.beforeEach(async ({ page }) => { - poLiveChat = new OmnichannelLiveChat(page); + test.beforeEach(async ({ page, api }) => { + poLiveChat = new OmnichannelLiveChat(page, api); }); test.afterAll(async ({ api }) => { @@ -55,7 +55,7 @@ test.describe.serial('omnichannel-triggers', () => { await test.step('expect triggers to be displaye on Livechat', async () => { await test.step('Expect send a message as a visitor', async () => { await page.goto('/livechat'); - await poLiveChat.btnOpenLiveChat('R').click(); + await poLiveChat.openLiveChat(); if (await page.locator('[type="button"] >> text="Chat now"').isVisible()) { await page.locator('[type="button"] >> text="Chat now"').click(); } diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-livechat.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-livechat.ts index 510028168013..b8143f74f129 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-livechat.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-livechat.ts @@ -1,9 +1,9 @@ -import type { Page, Locator } from '@playwright/test'; +import type { Page, Locator, APIResponse } from '@playwright/test'; export class OmnichannelLiveChat { private readonly page: Page; - constructor(page: Page) { + constructor(page: Page, private readonly api: { get(url: string): Promise }) { this.page = page; } @@ -11,6 +11,11 @@ export class OmnichannelLiveChat { return this.page.locator(`role=button[name="${label}"]`); } + async openLiveChat(): Promise { + const { value: siteName } = await (await this.api.get('/settings/Site_Name')).json(); + await this.btnOpenLiveChat(siteName).click(); + } + unreadMessagesBadge(count: number): Locator { const name = count === 1 ? `${count} unread message` : `${count} unread messages`; diff --git a/package.json b/package.json index d97fc268fee2..97f4e1780418 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ }, "resolutions": { "minimist": "1.2.6", - "adm-zip": "0.5.9" + "adm-zip": "0.5.9", + "preact@10.15.1": "patch:preact@npm:10.15.1#.yarn/patches/preact-npm-10.15.1-bd458de913.patch" } } diff --git a/packages/livechat/.storybook/main.js b/packages/livechat/.storybook/main.js index 7aa6ba647b1d..bae78807f8b4 100644 --- a/packages/livechat/.storybook/main.js +++ b/packages/livechat/.storybook/main.js @@ -6,8 +6,7 @@ module.exports = { backgrounds: false, }, }, - '@storybook/addon-knobs', '@storybook/addon-postcss', ], - stories: ['../src/**/stories.js', '../src/**/story.js', '../src/**/*.stories.js', '../src/**/*.story.js'], + stories: ['../src/**/stories.{js,tsx}', '../src/**/story.{js,tsx}', '../src/**/*.stories.{js,tsx}', '../src/**/*.story.{js,tsx}'], }; diff --git a/packages/livechat/babel.config.js b/packages/livechat/babel.config.js index 37654a3a204c..ce989051a52f 100644 --- a/packages/livechat/babel.config.js +++ b/packages/livechat/babel.config.js @@ -1,32 +1,15 @@ module.exports = { - presets: [ - [ - '@babel/preset-env', - { - useBuiltIns: 'entry', - corejs: 3, - include: [ - '@babel/plugin-proposal-class-properties', - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - ], - }, - ], - '@babel/preset-typescript', - ], + presets: [['@babel/preset-env', { loose: true }], '@babel/preset-typescript'], plugins: [ - ['@babel/plugin-transform-react-jsx', { pragma: 'h', pragmaFrag: 'Fragment' }], + ['@babel/plugin-transform-class-properties', { loose: true }], + ['@babel/plugin-transform-private-methods', { loose: true }], + ['@babel/plugin-transform-private-property-in-object', { loose: true }], [ - 'babel-plugin-jsx-pragmatic', + '@babel/plugin-transform-react-jsx', { - module: 'preact', - import: 'h', - export: 'h', + runtime: 'automatic', + importSource: 'preact', }, ], ], - assumptions: { - setPublicClassFields: true, - privateFieldsAsProperties: true, - }, }; diff --git a/packages/livechat/package.json b/packages/livechat/package.json index 26940f22c846..775a44b6dad8 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -19,30 +19,26 @@ "lint": "run-s eslint stylelint", "eslint": "eslint --ext .js,.jsx,.ts,.tsx .", "stylelint": "stylelint 'src/**/*.scss'", - "storybook": "start-storybook -p 9001 -c .storybook", + "storybook": "start-storybook -p 9001 -c .storybook --no-version-updates", "build-storybook": "build-storybook", "typecheck": "tsc -p tsconfig.typecheck.json" }, "devDependencies": { "@babel/eslint-parser": "~7.22.5", "@babel/preset-env": "~7.22.5", + "@babel/preset-typescript": "~7.22.5", "@rocket.chat/ddp-client": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/fuselage-tokens": "next", "@rocket.chat/logo": "next", - "@storybook/addon-actions": "~6.5.16", - "@storybook/addon-backgrounds": "~6.5.16", "@storybook/addon-essentials": "~6.5.16", - "@storybook/addon-knobs": "~6.4.0", "@storybook/addon-postcss": "~2.0.0", - "@storybook/addon-viewport": "~6.5.16", - "@storybook/react": "~6.5.16", + "@storybook/preact": "~6.5.16", "@storybook/theming": "~6.5.16", "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", "autoprefixer": "^9.8.8", "babel-loader": "^8.3.0", - "babel-plugin-jsx-pragmatic": "^1.0.2", "cross-env": "^7.0.3", "css-loader": "^4.3.0", "cssnano": "^4.1.11", @@ -99,16 +95,15 @@ "markdown-it": "^11.0.1", "mem": "^6.1.1", "mitt": "^2.1.0", - "preact": "^10.8.2", + "preact": "10.15.1", "preact-router": "^3.2.1", "query-string": "^7.1.3", "react-i18next": "^11.16.9", "whatwg-fetch": "^3.6.2" }, "browserslist": [ - "> 1%", "last 2 versions", - "not ie < 11" + "Firefox ESR" ], "volta": { "extends": "../../package.json" diff --git a/packages/livechat/src/components/Alert/index.tsx b/packages/livechat/src/components/Alert/index.tsx index c68c88e5a58d..fc568ae8022b 100644 --- a/packages/livechat/src/components/Alert/index.tsx +++ b/packages/livechat/src/components/Alert/index.tsx @@ -1,3 +1,4 @@ +import type { ComponentChildren } from 'preact'; import { useCallback, useEffect } from 'preact/hooks'; import type { JSXInternal } from 'preact/src/jsx'; import { useTranslation } from 'react-i18next'; @@ -16,7 +17,7 @@ type AlertProps = { hideCloseButton?: boolean; className?: string; style?: JSXInternal.CSSProperties; - children?: JSXInternal.Element[]; + children?: ComponentChildren; timeout?: number; }; diff --git a/packages/livechat/src/components/Alert/stories.js b/packages/livechat/src/components/Alert/stories.js deleted file mode 100644 index 10dd0044696c..000000000000 --- a/packages/livechat/src/components/Alert/stories.js +++ /dev/null @@ -1,82 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, color, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import Alert from '.'; -import { screenCentered, loremIpsum } from '../../helpers.stories'; - -storiesOf('Components/Alert', module) - .addDecorator(withKnobs) - .addDecorator(screenCentered) - .add('default', () => ( - - {text('text', loremIpsum({ count: 3, units: 'words' }))} - - )) - .add('success', () => ( - - {text('text', loremIpsum({ count: 3, units: 'words' }))} - - )) - .add('warning', () => ( - - {text('text', loremIpsum({ count: 3, units: 'words' }))} - - )) - .add('error', () => ( - - {text('text', loremIpsum({ count: 3, units: 'words' }))} - - )) - .add('custom color', () => ( - - {text('text', loremIpsum({ count: 3, units: 'words' }))} - - )) - .add('with long text content', () => ( - - {text('text', loremIpsum({ count: 30, units: 'words' }))} - - )) - .add('without timeout', () => ( - - {text('text', loremIpsum({ count: 3, units: 'words' }))} - - )); diff --git a/packages/livechat/src/components/Alert/stories.tsx b/packages/livechat/src/components/Alert/stories.tsx new file mode 100644 index 000000000000..6e63e98400ed --- /dev/null +++ b/packages/livechat/src/components/Alert/stories.tsx @@ -0,0 +1,66 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import Alert from '.'; +import { loremIpsum, screenDecorator } from '../../helpers.stories'; + +export default { + title: 'Components/Alert', + component: Alert, + args: { + success: false, + warning: false, + error: false, + color: '', + timeout: 5000, + onDismiss: action('dismiss'), + children: loremIpsum({ count: 3, units: 'words' }), + }, + parameters: { + layout: 'centered', + }, + decorators: [screenDecorator], +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; +Default.args = {}; + +export const Success = Template.bind({}); +Success.storyName = 'success'; +Success.args = { + success: true, +}; + +export const Warning = Template.bind({}); +Warning.storyName = 'warning'; +Warning.args = { + warning: true, +}; + +export const Error = Template.bind({}); +Error.storyName = 'error'; +Error.args = { + error: true, +}; + +export const CustomColor = Template.bind({}); +CustomColor.storyName = 'custom color'; +CustomColor.args = { + color: '#175CC4', +}; + +export const WithLongTextContent = Template.bind({}); +WithLongTextContent.storyName = 'with long text content'; +WithLongTextContent.args = { + children: loremIpsum({ count: 30, units: 'words' }), +}; + +export const WithoutTimeout = Template.bind({}); +WithoutTimeout.storyName = 'without timeout'; +WithoutTimeout.args = { + timeout: 0, +}; diff --git a/packages/livechat/src/components/Avatar/stories.js b/packages/livechat/src/components/Avatar/stories.js deleted file mode 100644 index 0cdb8730a026..000000000000 --- a/packages/livechat/src/components/Avatar/stories.js +++ /dev/null @@ -1,92 +0,0 @@ -import { withKnobs, boolean, text, select } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { Avatar } from '.'; -import { avatarResolver, centered } from '../../helpers.stories'; - -const defaultSrc = avatarResolver('guilherme.gazzo'); -const defaultDescription = 'user description'; -const statuses = [null, 'offline', 'away', 'busy', 'online']; - -storiesOf('Components/Avatar', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ( - - )) - .add('large', () => ( - - )) - .add('small', () => ( - - )) - .add('as placeholder', () => ( -

- - - -
- )) - .add('with status indicator', () => ( -
- - - - -
- )); diff --git a/packages/livechat/src/components/Avatar/stories.tsx b/packages/livechat/src/components/Avatar/stories.tsx new file mode 100644 index 000000000000..ac2d6bc6b9c9 --- /dev/null +++ b/packages/livechat/src/components/Avatar/stories.tsx @@ -0,0 +1,66 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { Avatar } from '.'; +import { gazzoAvatar } from '../../helpers.stories'; + +export default { + title: 'Components/Avatar', + component: Avatar, + args: { + src: gazzoAvatar, + description: 'user description', + status: null, + large: false, + }, + argTypes: { + status: { + control: { + type: 'select', + options: [null, 'offline', 'away', 'busy', 'online'], + }, + }, + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; + +export const Large = Template.bind({}); +Large.storyName = 'large'; +Large.args = { + large: true, +}; + +export const Small = Template.bind({}); +Small.storyName = 'small'; +Small.args = { + small: true, +}; + +export const AsPlaceholder: Story> = (args) => ( +
+ + + +
+); +AsPlaceholder.storyName = 'as placeholder'; +AsPlaceholder.args = { + src: '', +}; + +export const WithStatusIndicator: Story> = (args) => ( +
+ + + + +
+); +WithStatusIndicator.storyName = 'with status indicator'; diff --git a/packages/livechat/src/components/Button/index.tsx b/packages/livechat/src/components/Button/index.tsx index d7d957d23acf..d774bc78b6ec 100644 --- a/packages/livechat/src/components/Button/index.tsx +++ b/packages/livechat/src/components/Button/index.tsx @@ -1,3 +1,4 @@ +import type { ComponentChildren } from 'preact'; import type { CSSProperties } from 'preact/compat'; import type { JSXInternal } from 'preact/src/jsx'; import { useTranslation } from 'react-i18next'; @@ -9,6 +10,7 @@ const handleMouseUp: JSXInternal.EventHandler; + icon?: ComponentChildren; className?: string; style?: CSSProperties; - children?: JSXInternal.Element[]; img?: string; + onClick?: JSXInternal.MouseEventHandler; }; export const Button = ({ @@ -52,7 +53,7 @@ export const Button = ({ disabled={disabled} onClick={onClick} onMouseUp={handleMouseUp} - aria-label={icon && children ? children[0] : null} + aria-label={icon && Array.isArray(children) ? children[0] : children} className={createClassName( styles, 'button', diff --git a/packages/livechat/src/components/Button/stories.js b/packages/livechat/src/components/Button/stories.js deleted file mode 100644 index cfe5e09c9a45..000000000000 --- a/packages/livechat/src/components/Button/stories.js +++ /dev/null @@ -1,211 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { Button } from '.'; -import { avatarResolver, centered } from '../../helpers.stories'; -import ChatIcon from '../../icons/chat.svg'; - -const defaultSrc = avatarResolver('guilherme.gazzo'); - -const defaultText = 'Powered by Rocket.Chat'; -const defaultBadge = 'badged'; - -storiesOf('Components/Button', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('normal', () => ( - - )) - .add('disabled', () => ( - - )) - .add('outline', () => ( - - )) - .add('nude', () => ( - - )) - .add('danger', () => ( - - )) - .add('secondary', () => ( - - )) - .add('stack', () => ( - - )) - .add('small', () => ( - - )) - .add('loading', () => ( - - )) - .add('with badge', () => ( - - )) - .add('with icon', () => ( - - )) - .add('transparent with background image', () => ( - - )); diff --git a/packages/livechat/src/components/Button/stories.tsx b/packages/livechat/src/components/Button/stories.tsx new file mode 100644 index 000000000000..42bdee65166b --- /dev/null +++ b/packages/livechat/src/components/Button/stories.tsx @@ -0,0 +1,112 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { Button } from '.'; +import { gazzoAvatar } from '../../helpers.stories'; +import ChatIcon from '../../icons/chat.svg'; + +const iconElement = ; + +export default { + title: 'components/Button', + component: Button, + args: { + disabled: false, + outline: false, + nude: false, + danger: false, + secondary: false, + stack: false, + small: false, + loading: false, + badge: undefined, + icon: null, + img: undefined, + children: 'Powered by Rocket.Chat', + onClick: action('clicked'), + }, + argTypes: { + icon: { + control: { + type: 'select', + options: [null, iconElement], + }, + }, + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => - - - - )) - .add('with buttons of different sizes', () => ( - - - - - - )) - .add('with only small buttons', () => ( - - - - - - )) - .add('with stacked buttons', () => ( - - - - - - )); diff --git a/packages/livechat/src/components/ButtonGroup/stories.tsx b/packages/livechat/src/components/ButtonGroup/stories.tsx new file mode 100644 index 000000000000..91b516ed7cda --- /dev/null +++ b/packages/livechat/src/components/ButtonGroup/stories.tsx @@ -0,0 +1,62 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { ButtonGroup } from '.'; +import { Button } from '../Button'; + +/** @type {import('@storybook/preact').Meta>} */ +export default { + title: 'Components/ButtonGroup', + component: ButtonGroup, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +export const WithButtonsOfSameSize: Story> = (args) => ( + + + + + +); +WithButtonsOfSameSize.storyName = 'with buttons of same size'; + +export const WithButtonsOfDifferentSizes: Story> = (args) => ( + + + + + +); +WithButtonsOfDifferentSizes.storyName = 'with buttons of different sizes'; + +export const WithOnlySmallButtons: Story> = (args) => ( + + + + + +); +WithOnlySmallButtons.storyName = 'with only small buttons'; + +export const WithStackedButtons: Story> = (args) => ( + + + + + +); +WithStackedButtons.storyName = 'with stacked buttons'; diff --git a/packages/livechat/src/components/Composer/stories.js b/packages/livechat/src/components/Composer/stories.js deleted file mode 100644 index 39f5460fd334..000000000000 --- a/packages/livechat/src/components/Composer/stories.js +++ /dev/null @@ -1,89 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { Composer, ComposerActions, ComposerAction } from '.'; -import { centered } from '../../helpers.stories'; -import PlusIcon from '../../icons/plus.svg'; -import SendIcon from '../../icons/send.svg'; -import SmileIcon from '../../icons/smile.svg'; - -const centeredWithWidth = (storyFn, ...args) => centered(() =>
{storyFn()}
, ...args); - -const defaultPlaceholder = 'Insert your text here'; - -storiesOf('Components/Composer', module) - .addDecorator(centeredWithWidth) - .addDecorator(withKnobs({ escapeHTML: false })) - .add('default', () => ( - - )) - .add('connecting', () => ) - .add('with large placeholder', () => ( - - )) - .add('with plain text', () => ( - - )) - .add('with emojis', () => ( - - )) - .add('with mentions', () => ( - - )) - .add('with actions', () => ( - - - - - - - - - } - post={ - - - - - - } - onChange={action('change')} - onSubmit={action('submit')} - onUpload={action('upload')} - /> - )); diff --git a/packages/livechat/src/components/Composer/stories.tsx b/packages/livechat/src/components/Composer/stories.tsx new file mode 100644 index 000000000000..0386602b5014 --- /dev/null +++ b/packages/livechat/src/components/Composer/stories.tsx @@ -0,0 +1,77 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { Composer, ComposerActions, ComposerAction } from '.'; +import PlusIcon from '../../icons/plus.svg'; +import SendIcon from '../../icons/send.svg'; +import SmileIcon from '../../icons/smile.svg'; + +const defaultPlaceholder = 'Insert your text here'; + +export default { + title: 'Components/Composer', + component: Composer, + args: { + value: '', + placeholder: 'Insert your text here', + onChange: action('change'), + onSubmit: action('submit'), + onUpload: action('upload'), + }, + decorators: [(storyFn) =>
{storyFn()}
], + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; + +export const WithLargePlaceholder = Template.bind({}); +WithLargePlaceholder.storyName = 'with large placeholder'; +WithLargePlaceholder.args = { + placeholder: new Array(5).fill(defaultPlaceholder).join(' '), +}; + +export const WithPlainText = Template.bind({}); +WithPlainText.storyName = 'with plain text'; +WithPlainText.args = { + value: 'Should I use & or &?', +}; + +export const WithEmojis = Template.bind({}); +WithEmojis.storyName = 'with emojis'; +WithEmojis.args = { + value: ":heart: :smile: :'(", +}; + +export const WithMentions = Template.bind({}); +WithMentions.storyName = 'with mentions'; +WithMentions.args = { + value: "@all, I'm @here with @user.", +}; + +export const WithActions = Template.bind({}); +WithActions.storyName = 'with actions'; +WithActions.args = { + pre: ( + + + + + + + + + ), + post: ( + + + + + + ), +}; diff --git a/packages/livechat/src/components/FilesDropTarget/stories.js b/packages/livechat/src/components/FilesDropTarget/stories.js deleted file mode 100644 index acd2a32a331b..000000000000 --- a/packages/livechat/src/components/FilesDropTarget/stories.js +++ /dev/null @@ -1,103 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, button, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { FilesDropTarget } from '.'; -import { centered } from '../../helpers.stories'; - -const DummyContent = () => ( -
- Drop files here - Or into this span -
-); - -storiesOf('Components/FilesDropTarget', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ( - - - - )) - .add('overlayed', () => ( - - - - )) - .add('overlayed with text', () => ( - - - - )) - .add('accepting only images', () => ( - - - - )) - .add('accepting multiple', () => ( - - - - )) - .add('triggering browse action', () => { - let filesDropTarget; - - function handleRef(ref) { - filesDropTarget = ref; - } - - button('Browse for files', () => { - filesDropTarget.browse(); - }); - - return ( - - ); - }); diff --git a/packages/livechat/src/components/FilesDropTarget/stories.tsx b/packages/livechat/src/components/FilesDropTarget/stories.tsx new file mode 100644 index 000000000000..e1a01cbaea09 --- /dev/null +++ b/packages/livechat/src/components/FilesDropTarget/stories.tsx @@ -0,0 +1,91 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; +import { createRef } from 'preact'; + +import { FilesDropTarget } from '.'; +import { Button } from '../Button'; + +const DummyContent = () => ( +
+ Drop files here + Or into this span +
+); + +export default { + title: 'Components/FilesDropTarget', + component: FilesDropTarget, + args: { + children: , + overlayed: false, + overlayText: '', + accept: '', + multiple: false, + onUpload: action('upload'), + }, + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; +Default.args = {}; + +export const Overlayed = Template.bind({}); +Overlayed.storyName = 'overlayed'; +Overlayed.args = { + overlayed: true, +}; + +export const OverlayedWithText = Template.bind({}); +OverlayedWithText.storyName = 'overlayed with text'; +OverlayedWithText.args = { + overlayed: true, + overlayText: 'You can release your files now', +}; + +export const AcceptingOnlyImages = Template.bind({}); +AcceptingOnlyImages.storyName = 'accepting only images'; +AcceptingOnlyImages.args = { + accept: 'image/*', +}; + +export const AcceptingMultipleFiles = Template.bind({}); +AcceptingMultipleFiles.storyName = 'accepting multiple files'; +AcceptingMultipleFiles.args = { + multiple: true, +}; + +export const TriggeringBrowseAction = Template.bind({}); +TriggeringBrowseAction.storyName = 'triggering browse action'; +const ref = createRef(); +TriggeringBrowseAction.args = { + children: ( +
+ +
+ ), + ref, +}; diff --git a/packages/livechat/src/components/Footer/index.js b/packages/livechat/src/components/Footer/index.js index dd7270cd7ca4..78b023d5d872 100644 --- a/packages/livechat/src/components/Footer/index.js +++ b/packages/livechat/src/components/Footer/index.js @@ -11,7 +11,7 @@ export const Footer = ({ children, className, ...props }) => ( ); -export const FooterContent = ({ children, className, ...props }) => ( +export const FooterContent = ({ children, className = undefined, ...props }) => (
{children}
diff --git a/packages/livechat/src/components/Footer/stories.js b/packages/livechat/src/components/Footer/stories.js deleted file mode 100644 index d9c813e4a26f..000000000000 --- a/packages/livechat/src/components/Footer/stories.js +++ /dev/null @@ -1,53 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { storiesOf } from '@storybook/react'; -import '../../i18next'; - -import { Footer, FooterContent, FooterOptions, PoweredBy } from '.'; -import ChangeIcon from '../../icons/change.svg'; -import FinishIcon from '../../icons/finish.svg'; -import RemoveIcon from '../../icons/remove.svg'; -import { Composer } from '../Composer'; -import Menu from '../Menu'; -import { PopoverContainer } from '../Popover'; - -const bottomWithPopoverContainer = (storyFn) => ( -
- -
- {storyFn()} - -
-); - -storiesOf('Components/Footer', module) - .addDecorator(bottomWithPopoverContainer) - .add('simple', () => ( -
- - - -
- )) - .add('with Composer and options', () => ( -
- - - - - - - - Change department - - - Forget/Remove my personal data - - - Finish this chat - - - - - -
- )); diff --git a/packages/livechat/src/components/Footer/stories.tsx b/packages/livechat/src/components/Footer/stories.tsx new file mode 100644 index 000000000000..0f3a5e2f7961 --- /dev/null +++ b/packages/livechat/src/components/Footer/stories.tsx @@ -0,0 +1,65 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { Footer, FooterContent, FooterOptions, PoweredBy } from '.'; +import ChangeIcon from '../../icons/change.svg'; +import FinishIcon from '../../icons/finish.svg'; +import RemoveIcon from '../../icons/remove.svg'; +import { Composer } from '../Composer'; +import Menu from '../Menu'; +import { PopoverContainer } from '../Popover'; + +import '../../i18next'; + +export default { + title: 'Components/Footer', + component: Footer, + decorators: [ + (storyFn) => ( +
+ +
+ {storyFn()} + +
+ ), + ], + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta>; + +export const Simple: Story> = (args) => ( +
+ + + +
+); +Simple.storyName = 'simple'; + +export const WithComposerAndOptions: Story> = (args) => ( +
+ + + + + + + + Change department + + + Forget/Remove my personal data + + + Finish this chat + + + + + +
+); +WithComposerAndOptions.storyName = 'with Composer and options'; diff --git a/packages/livechat/src/components/Form/DateInput/stories.js b/packages/livechat/src/components/Form/DateInput/stories.js deleted file mode 100644 index 135cf6e3a3cf..000000000000 --- a/packages/livechat/src/components/Form/DateInput/stories.js +++ /dev/null @@ -1,87 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import DateInput from '.'; -import { Form, FormField } from '..'; - -storiesOf('Forms/DateInput', module) - .addDecorator(withKnobs) - .addParameters({ - layout: 'centered', - }) - .add('default', () => ( -
- - - -
- )) - .add('filled', () => ( -
- - - -
- )) - .add('disabled', () => ( -
- - - -
- )) - .add('small', () => ( -
- - - -
- )) - .add('with error', () => ( -
- - - -
- )); diff --git a/packages/livechat/src/components/Form/DateInput/stories.tsx b/packages/livechat/src/components/Form/DateInput/stories.tsx new file mode 100644 index 000000000000..deeca2c4fad8 --- /dev/null +++ b/packages/livechat/src/components/Form/DateInput/stories.tsx @@ -0,0 +1,59 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import DateInput from '.'; +import { Form, FormField } from '..'; + +export default { + title: 'Forms/DateInput', + component: DateInput, + args: { + value: '', + placeholder: 'Placeholder', + disabled: false, + small: false, + error: false, + onChange: action('change'), + onInput: action('input'), + }, + decorators: [ + (storyFn) => ( +
+ {storyFn()} +
+ ), + ], + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; + +export const Filled = Template.bind({}); +Filled.storyName = 'filled'; +Filled.args = { + value: 'Value', +}; + +export const Disabled = Template.bind({}); +Disabled.storyName = 'disabled'; +Disabled.args = { + disabled: true, +}; + +export const Small = Template.bind({}); +Small.storyName = 'small'; +Small.args = { + small: true, +}; + +export const WithError = Template.bind({}); +WithError.storyName = 'with error'; +WithError.args = { + error: true, +}; diff --git a/packages/livechat/src/components/Form/FormField/index.js b/packages/livechat/src/components/Form/FormField/index.js index 52f6f0f83814..ba85500462e6 100644 --- a/packages/livechat/src/components/Form/FormField/index.js +++ b/packages/livechat/src/components/Form/FormField/index.js @@ -3,7 +3,7 @@ import { cloneElement } from 'preact'; import { createClassName } from '../../helpers'; import styles from './styles.scss'; -export const FormField = ({ required, label, description, error, className, style = {}, children }) => ( +export const FormField = ({ required = false, label = '', description = '', error = '', className = undefined, style = {}, children }) => (
- )); - -const centeredWithPopoverContainer = (storyFn, ...args) => ( -
- {centered(storyFn, ...args)} -
-); - -storiesOf('Components/Menu/PopoverMenu', module) - .addDecorator(withKnobs) - .addDecorator(centeredWithPopoverContainer) - .add('default', () => ( - } - > - - Reload - Delete... - - - )) - .add('with overlay', () => ( - } - > - - Reload - Delete... - - - )); -storiesOf('Components/Menu/Group', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('single', () => ( - - )) - .add('multiple', () => ( - - )) - .add('with title', () => ( - - )); -storiesOf('Components/Menu/Item', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('simple', () => ( - - )) - .add('primary', () => ( - - )) - .add('danger', () => ( - - )) - .add('disabled', () => ( - - )) - .add('with icon', () => ( - - )); diff --git a/packages/livechat/src/components/Messages/AudioAttachment/stories.js b/packages/livechat/src/components/Messages/AudioAttachment/stories.js deleted file mode 100644 index 1c1923bfcc1b..000000000000 --- a/packages/livechat/src/components/Messages/AudioAttachment/stories.js +++ /dev/null @@ -1,11 +0,0 @@ -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import AudioAttachment from '.'; -import sampleAudio from '../../../../.storybook/assets/sample-audio.mp3'; -import { centered } from '../../../helpers.stories'; - -storiesOf('Messages/AudioAttachment', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ); diff --git a/packages/livechat/src/components/Messages/AudioAttachment/stories.tsx b/packages/livechat/src/components/Messages/AudioAttachment/stories.tsx new file mode 100644 index 000000000000..a07b6671cc15 --- /dev/null +++ b/packages/livechat/src/components/Messages/AudioAttachment/stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import AudioAttachment from '.'; +import { sampleAudio } from '../../../helpers.stories'; + +export default { + title: 'Messages/AudioAttachment', + component: AudioAttachment, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +export const Default: Story> = (args) => ; +Default.storyName = 'default'; +Default.args = { + url: sampleAudio, +}; diff --git a/packages/livechat/src/components/Messages/FileAttachment/stories.js b/packages/livechat/src/components/Messages/FileAttachment/stories.js deleted file mode 100644 index c7e130b96df4..000000000000 --- a/packages/livechat/src/components/Messages/FileAttachment/stories.js +++ /dev/null @@ -1,20 +0,0 @@ -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { FileAttachment } from '.'; -import { loremIpsum, centered } from '../../../helpers.stories'; - -const centeredWithWidth = (storyFn, ...args) => centered(() =>
{storyFn()}
, ...args); - -storiesOf('Messages/FileAttachment', module) - .addDecorator(centeredWithWidth) - .addDecorator(withKnobs) - .add('for pdf', () => ) - .add('for doc', () => ) - .add('for ppt', () => ) - .add('for xls', () => ) - .add('for zip', () => ) - .add('for unknown extension', () => ) - .add('with long title', () => ( - - )); diff --git a/packages/livechat/src/components/Messages/FileAttachment/stories.tsx b/packages/livechat/src/components/Messages/FileAttachment/stories.tsx new file mode 100644 index 000000000000..d342d523b4e0 --- /dev/null +++ b/packages/livechat/src/components/Messages/FileAttachment/stories.tsx @@ -0,0 +1,62 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { FileAttachment } from '.'; +import { loremIpsum } from '../../../helpers.stories'; + +export default { + title: 'Messages/FileAttachment', + component: FileAttachment, + args: { + title: 'Untitled', + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const PDF = Template.bind({}); +PDF.storyName = 'for pdf'; +PDF.args = { + url: 'http://example.com/demo.pdf', +}; + +export const DOC = Template.bind({}); +DOC.storyName = 'for doc'; +DOC.args = { + url: 'http://example.com/demo.doc', +}; + +export const PPT = Template.bind({}); +PPT.storyName = 'for ppt'; +PPT.args = { + url: 'http://example.com/demo.ppt', +}; + +export const XLS = Template.bind({}); +XLS.storyName = 'for xls'; +XLS.args = { + url: 'http://example.com/demo.xls', +}; + +export const ZIP = Template.bind({}); +ZIP.storyName = 'for zip'; +ZIP.args = { + url: 'http://example.com/demo.zip', +}; + +export const UnknownExtension = Template.bind({}); +UnknownExtension.storyName = 'for unknown extension'; +UnknownExtension.args = { + url: 'http://example.com/demo.abc', +}; + +export const WithLongTitle = Template.bind({}); +WithLongTitle.storyName = 'with long title'; +WithLongTitle.args = { + title: loremIpsum({ count: 50, units: 'words' }), + url: 'http://example.com/demo.abc', +}; +WithLongTitle.decorators = [(storyFn) =>
{storyFn()}
]; diff --git a/packages/livechat/src/components/Messages/ImageAttachment/stories.js b/packages/livechat/src/components/Messages/ImageAttachment/stories.js deleted file mode 100644 index 538867d972ec..000000000000 --- a/packages/livechat/src/components/Messages/ImageAttachment/stories.js +++ /dev/null @@ -1,11 +0,0 @@ -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { ImageAttachment } from '.'; -import sampleImage from '../../../../.storybook/assets/sample-image.jpg'; -import { centered } from '../../../helpers.stories'; - -storiesOf('Messages/ImageAttachment', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ); diff --git a/packages/livechat/src/components/Messages/ImageAttachment/stories.tsx b/packages/livechat/src/components/Messages/ImageAttachment/stories.tsx new file mode 100644 index 000000000000..fc3172729076 --- /dev/null +++ b/packages/livechat/src/components/Messages/ImageAttachment/stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { ImageAttachment } from '.'; +import { sampleImage } from '../../../helpers.stories'; + +export default { + title: 'Messages/ImageAttachment', + component: ImageAttachment, + args: { + url: sampleImage, + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +export const Default: Story> = (args) => ; +Default.storyName = 'default'; diff --git a/packages/livechat/src/components/Messages/Message/stories.js b/packages/livechat/src/components/Messages/Message/stories.tsx similarity index 79% rename from packages/livechat/src/components/Messages/Message/stories.js rename to packages/livechat/src/components/Messages/Message/stories.tsx index 5237af9e0320..1b07dbf96f92 100644 --- a/packages/livechat/src/components/Messages/Message/stories.js +++ b/packages/livechat/src/components/Messages/Message/stories.tsx @@ -1,8 +1,8 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + import Message from '.'; -import sampleAudio from '../../../../.storybook/assets/sample-audio.mp3'; -import sampleImage from '../../../../.storybook/assets/sample-image.jpg'; -import sampleVideo from '../../../../.storybook/assets/sample-video.mp4'; -import { attachmentResolver, avatarResolver, loremIpsum } from '../../../helpers.stories'; +import { attachmentResolver, avatarResolver, loremIpsum, sampleAudio, sampleImage, sampleVideo } from '../../../helpers.stories'; import { MESSAGE_TYPE_ROOM_NAME_CHANGED, MESSAGE_TYPE_USER_ADDED, @@ -14,18 +14,6 @@ import { MESSAGE_TYPE_LIVECHAT_TRANSFER_HISTORY, } from '../constants'; -const messageTypes = { - NULL: null, - ROOM_NAME_CHANGED: MESSAGE_TYPE_ROOM_NAME_CHANGED, - USER_ADDED: MESSAGE_TYPE_USER_ADDED, - USER_REMOVED: MESSAGE_TYPE_USER_REMOVED, - USER_JOINED: MESSAGE_TYPE_USER_JOINED, - USER_LEFT: MESSAGE_TYPE_USER_LEFT, - WELCOME: MESSAGE_TYPE_WELCOME, - LIVECHAT_CLOSED: MESSAGE_TYPE_LIVECHAT_CLOSED, - TRANSFER_HISTORY: MESSAGE_TYPE_LIVECHAT_TRANSFER_HISTORY, -}; - const defaultMessage = loremIpsum({ count: 1, units: 'sentences' }); const defaultMessageExtra = loremIpsum({ count: 1, units: 'sentences' }); @@ -75,7 +63,22 @@ export default { me: { control: 'boolean' }, compact: { control: 'boolean' }, msg: { control: 'text' }, - t: { control: { type: 'select', options: messageTypes } }, + t: { + control: { + type: 'select', + options: { + NULL: null, + ROOM_NAME_CHANGED: MESSAGE_TYPE_ROOM_NAME_CHANGED, + USER_ADDED: MESSAGE_TYPE_USER_ADDED, + USER_REMOVED: MESSAGE_TYPE_USER_REMOVED, + USER_JOINED: MESSAGE_TYPE_USER_JOINED, + USER_LEFT: MESSAGE_TYPE_USER_LEFT, + WELCOME: MESSAGE_TYPE_WELCOME, + LIVECHAT_CLOSED: MESSAGE_TYPE_LIVECHAT_CLOSED, + TRANSFER_HISTORY: MESSAGE_TYPE_LIVECHAT_TRANSFER_HISTORY, + }, + }, + }, u: { control: 'object' }, ts: { control: 'date' }, attachments: { control: 'object' }, @@ -91,9 +94,9 @@ export default { attachments: [], blocks: [], }, -}; +} satisfies Meta>; -export const Default = (args) => ( +export const Default: Story> = (args) => ( ( ); Default.storyName = 'default'; -export const System = (args) => ( +export const System: Story> = (args) => ( ( +export const Me: Story> = (args) => ( ( +export const Markdown: Story> = (args) => ( ( +export const Grouping: Story> = (args) => (
( +export const WithQuotation: Story> = (args) => ( ( +export const WithAudioAttachment: Story> = (args) => ( ( +export const WithVideoAttachment: Story> = (args) => ( ( +export const WithImageAttachment: Story> = (args) => ( ( +export const WithFilesAttachments: Story> = (args) => ( ( +export const WithMultipleAttachments: Story> = (args) => ( ( +export const WithUiKitBlocks: Story> = (args) => ( ) - .add('with one avatar', () => ) - .add('with two avatars', () => ( - - )) - .add('with three avatars', () => ( - - )) - .add('with name as avatar instead of username for guests', () => ( - - )); diff --git a/packages/livechat/src/components/Messages/MessageAvatars/stories.tsx b/packages/livechat/src/components/Messages/MessageAvatars/stories.tsx new file mode 100644 index 000000000000..957df00a7f67 --- /dev/null +++ b/packages/livechat/src/components/Messages/MessageAvatars/stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { MessageAvatars } from '.'; +import { avatarResolver } from '../../../helpers.stories'; + +export default { + title: 'Messages/MessageAvatars', + component: MessageAvatars, + args: { + avatarResolver, + usernames: [], + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Empty = Template.bind({}); +Empty.storyName = 'empty'; + +export const WithOneAvatar = Template.bind({}); +WithOneAvatar.storyName = 'with one avatar'; +WithOneAvatar.args = { + usernames: ['guilherme.gazzo'], +}; + +export const WithTwoAvatars = Template.bind({}); +WithTwoAvatars.storyName = 'with two avatars'; +WithTwoAvatars.args = { + usernames: ['guilherme.gazzo', 'tasso.evangelista'], +}; + +export const WithThreeAvatars = Template.bind({}); +WithThreeAvatars.storyName = 'with three avatars'; +WithThreeAvatars.args = { + usernames: ['guilherme.gazzo', 'tasso.evangelista', 'martin.schoeler'], +}; + +export const WithNameAsAvatarInsteadOfUsernameForGuests = Template.bind({}); +WithNameAsAvatarInsteadOfUsernameForGuests.storyName = 'with name as avatar instead of username for guests'; +WithNameAsAvatarInsteadOfUsernameForGuests.args = { + usernames: ['livechat guest'], +}; diff --git a/packages/livechat/src/components/Messages/MessageBlocks/index.js b/packages/livechat/src/components/Messages/MessageBlocks/index.js index 5078155c2d33..5faa4c50d1ee 100644 --- a/packages/livechat/src/components/Messages/MessageBlocks/index.js +++ b/packages/livechat/src/components/Messages/MessageBlocks/index.js @@ -6,7 +6,7 @@ import { renderMessageBlocks } from '../../uiKit'; import Surface from '../../uiKit/message/Surface'; import styles from './styles.scss'; -const MessageBlocks = ({ blocks = [], mid, rid }) => { +const MessageBlocks = ({ blocks = [], mid = undefined, rid = undefined }) => { const dispatchAction = useCallback( ({ appId, actionId, payload }) => triggerAction({ diff --git a/packages/livechat/src/components/Messages/MessageBlocks/stories.js b/packages/livechat/src/components/Messages/MessageBlocks/stories.tsx similarity index 96% rename from packages/livechat/src/components/Messages/MessageBlocks/stories.js rename to packages/livechat/src/components/Messages/MessageBlocks/stories.tsx index df201dc4c6b9..ee534155bb4e 100644 --- a/packages/livechat/src/components/Messages/MessageBlocks/stories.js +++ b/packages/livechat/src/components/Messages/MessageBlocks/stories.tsx @@ -1,6 +1,8 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + import MessageBlocks from '.'; -import accessoryImage from '../../../../.storybook/assets/accessoryImage.png'; -import imageBlock from '../../../../.storybook/assets/imageBlock.png'; +import { accessoryImage, imageBlock } from '../../../helpers.stories'; import { PopoverContainer } from '../../Popover'; export default { @@ -9,9 +11,9 @@ export default { layout: 'fullscreen', }, decorators: [(storyFn) => ], -}; +} satisfies Meta>; -export const WithBlocks = () => ( +export const WithBlocks: Story> = () => ( ( - - {text} - - )) - .add('inverse', () => ( - - {text} - - )) - .add('nude', () => ( - - {text} - - )) - .add('quoted', () => ( - - {text} - - )); diff --git a/packages/livechat/src/components/Messages/MessageBubble/stories.tsx b/packages/livechat/src/components/Messages/MessageBubble/stories.tsx new file mode 100644 index 000000000000..34ccf2523962 --- /dev/null +++ b/packages/livechat/src/components/Messages/MessageBubble/stories.tsx @@ -0,0 +1,44 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { MessageBubble } from '.'; +import { loremIpsum } from '../../../helpers.stories'; + +const text = loremIpsum({ count: 1, units: 'sentences' }); + +export default { + title: 'Messages/MessageBubble', + component: MessageBubble, + args: { + children: text, + inverse: false, + nude: false, + quoted: false, + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; + +export const Inverse = Template.bind({}); +Inverse.storyName = 'inverse'; +Inverse.args = { + inverse: true, +}; + +export const Nude = Template.bind({}); +Nude.storyName = 'nude'; +Nude.args = { + nude: true, +}; + +export const Quoted = Template.bind({}); +Quoted.storyName = 'quoted'; +Quoted.args = { + quoted: true, +}; diff --git a/packages/livechat/src/components/Messages/MessageList/stories.js b/packages/livechat/src/components/Messages/MessageList/stories.js deleted file mode 100644 index 8a8ffee38b3b..000000000000 --- a/packages/livechat/src/components/Messages/MessageList/stories.js +++ /dev/null @@ -1,107 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, number, object } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { MessageList } from '.'; -import { avatarResolver, loremIpsum, centered } from '../../../helpers.stories'; -import { MESSAGE_TYPE_LIVECHAT_TRANSFER_HISTORY } from '../constants'; - -const fittingScreen = (storyFn, ...args) => - centered(() =>
{storyFn()}
, ...args); - -const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); - -const users = [ - { - _id: 1, - username: 'tasso.evangelista', - }, - { - _id: 2, - username: 'guilherme.gazzo', - }, - { - _id: 3, - username: 'martin.schoeler', - }, -]; - -const messages = new Array(10); -for (let i = 0; i < messages.length; ++i) { - messages[i] = { - _id: i + 1, - u: users[i % users.length], - msg: loremIpsum({ count: 1, units: 'sentences' }), - ts: new Date(now.getTime() - (15 - i) * 60000).toISOString(), - }; -} - -storiesOf('Messages/MessageList', module) - .addDecorator(fittingScreen) - .addDecorator(withKnobs) - .add('normal', () => ( - - )) - .add('with system message', () => ( - - )) - .add('with hidden agent info system message', () => ( - - )) - .add('with typing users', () => ( - - )); diff --git a/packages/livechat/src/components/Messages/MessageList/stories.tsx b/packages/livechat/src/components/Messages/MessageList/stories.tsx new file mode 100644 index 000000000000..f1b46a89e84e --- /dev/null +++ b/packages/livechat/src/components/Messages/MessageList/stories.tsx @@ -0,0 +1,99 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { MessageList } from '.'; +import { avatarResolver, loremIpsum } from '../../../helpers.stories'; +import { MESSAGE_TYPE_LIVECHAT_TRANSFER_HISTORY } from '../constants'; + +const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); + +const users = [ + { + _id: 1, + username: 'tasso.evangelista', + }, + { + _id: 2, + username: 'guilherme.gazzo', + }, + { + _id: 3, + username: 'martin.schoeler', + }, +]; + +const messages = new Array(10); +for (let i = 0; i < messages.length; ++i) { + messages[i] = { + _id: i + 1, + u: users[i % users.length], + msg: loremIpsum({ count: 1, units: 'sentences' }), + ts: new Date(now.getTime() - (15 - i) * 60000).toISOString(), + }; +} + +export default { + title: 'Messages/MessageList', + component: MessageList, + args: { + messages, + uid: 1, + avatarResolver, + lastReadMessageId: 7, + typingUsernames: [], + onScrollTo: action('scrollTo'), + }, + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; + +export const WithSystemMessage = Template.bind({}); +WithSystemMessage.storyName = 'with system message'; +WithSystemMessage.args = { + messages: [ + ...messages, + { + msg: '', + ts: now.toISOString(), + t: MESSAGE_TYPE_LIVECHAT_TRANSFER_HISTORY, + transferData: { + transferredBy: users[0], + scope: 'queue', + }, + u: users[0], + _id: 'AGiTzCjYyaypDxpDm', + }, + ], +}; + +export const WithHiddenAgentInfoSystemMessage = Template.bind({}); +WithHiddenAgentInfoSystemMessage.storyName = 'with hidden agent info system message'; +WithHiddenAgentInfoSystemMessage.args = { + messages: [ + ...messages, + { + msg: '', + t: MESSAGE_TYPE_LIVECHAT_TRANSFER_HISTORY, + transferData: { + transferredBy: { ...users[0], username: undefined }, + scope: 'queue', + }, + ts: now.toISOString(), + u: { ...users[0], username: undefined }, + _id: 'AGiTzCjYyaypDxpDm', + }, + ], +}; + +export const WithTypingUsers = Template.bind({}); +WithTypingUsers.storyName = 'with typing users'; +WithTypingUsers.args = { + typingUsernames: [users[1].username, users[2].username], +}; diff --git a/packages/livechat/src/components/Messages/MessageSeparator/stories.js b/packages/livechat/src/components/Messages/MessageSeparator/stories.js deleted file mode 100644 index 7a3dac28d07c..000000000000 --- a/packages/livechat/src/components/Messages/MessageSeparator/stories.js +++ /dev/null @@ -1,14 +0,0 @@ -import { withKnobs, boolean, date } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import MessageSeparator from '.'; -import { centered } from '../../../helpers.stories'; - -const defaultDate = new Date(Date.parse('2021-01-01T00:00:00.000Z')); - -storiesOf('Messages/MessageSeparator', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ) - .add('date', () => ) - .add('unread', () => ); diff --git a/packages/livechat/src/components/Messages/MessageSeparator/stories.tsx b/packages/livechat/src/components/Messages/MessageSeparator/stories.tsx new file mode 100644 index 000000000000..d55d85f745d4 --- /dev/null +++ b/packages/livechat/src/components/Messages/MessageSeparator/stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import MessageSeparator from '.'; + +export default { + title: 'Messages/MessageSeparator', + component: MessageSeparator, + args: { + date: null, + unread: false, + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; + +export const _Date = Template.bind({}); +_Date.storyName = 'date'; +_Date.args = { + date: new Date(Date.parse('2021-01-01T00:00:00.000Z')), +}; + +export const Unread = Template.bind({}); +Unread.storyName = 'unread'; +Unread.args = { + unread: true, +}; diff --git a/packages/livechat/src/components/Messages/MessageTime/stories.js b/packages/livechat/src/components/Messages/MessageTime/stories.js deleted file mode 100644 index 84eb51a263fc..000000000000 --- a/packages/livechat/src/components/Messages/MessageTime/stories.js +++ /dev/null @@ -1,14 +0,0 @@ -import { withKnobs, date } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import MessageTime from '.'; -import { centered } from '../../../helpers.stories'; - -const today = new Date(Date.parse('2021-01-01T00:00:00.000Z')); -const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000); - -storiesOf('Messages/MessageTime', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('today', () => ) - .add('yesterday', () => ); diff --git a/packages/livechat/src/components/Messages/MessageTime/stories.tsx b/packages/livechat/src/components/Messages/MessageTime/stories.tsx new file mode 100644 index 000000000000..7a3cbf571d8a --- /dev/null +++ b/packages/livechat/src/components/Messages/MessageTime/stories.tsx @@ -0,0 +1,29 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import MessageTime from '.'; + +const today = new Date(Date.parse('2021-01-01T00:00:00.000Z')); +const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000); + +export default { + title: 'Messages/MessageTime', + component: MessageTime, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Today = Template.bind({}); +Today.storyName = 'today'; +Today.args = { + ts: today, +}; + +export const Yesterday = Template.bind({}); +Yesterday.storyName = 'yesterday'; +Yesterday.args = { + ts: yesterday, +}; diff --git a/packages/livechat/src/components/Messages/TypingDots/stories.js b/packages/livechat/src/components/Messages/TypingDots/stories.js deleted file mode 100644 index c006b6ec6620..000000000000 --- a/packages/livechat/src/components/Messages/TypingDots/stories.js +++ /dev/null @@ -1,10 +0,0 @@ -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { TypingDots } from '.'; -import { centered } from '../../../helpers.stories'; - -storiesOf('Messages/TypingDots', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ); diff --git a/packages/livechat/src/components/Messages/TypingDots/stories.tsx b/packages/livechat/src/components/Messages/TypingDots/stories.tsx new file mode 100644 index 000000000000..f86e8a8904e4 --- /dev/null +++ b/packages/livechat/src/components/Messages/TypingDots/stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { TypingDots } from '.'; + +export default { + title: 'Messages/TypingDots', + component: TypingDots, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; +Default.args = { + text: 'The attendant is typing', +}; diff --git a/packages/livechat/src/components/Messages/TypingIndicator/stories.js b/packages/livechat/src/components/Messages/TypingIndicator/stories.js deleted file mode 100644 index 7ee4da241d37..000000000000 --- a/packages/livechat/src/components/Messages/TypingIndicator/stories.js +++ /dev/null @@ -1,19 +0,0 @@ -import { withKnobs, object, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { TypingIndicator } from '.'; -import { avatarResolver, centered } from '../../../helpers.stories'; - -storiesOf('Messages/TypingIndicator', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ( - - )) - .add('with avatars', () => ( - - )); diff --git a/packages/livechat/src/components/Messages/TypingIndicator/stories.tsx b/packages/livechat/src/components/Messages/TypingIndicator/stories.tsx new file mode 100644 index 000000000000..68a0c377565e --- /dev/null +++ b/packages/livechat/src/components/Messages/TypingIndicator/stories.tsx @@ -0,0 +1,29 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { TypingIndicator } from '.'; +import { avatarResolver } from '../../../helpers.stories'; + +export default { + title: 'Messages/TypingIndicator', + component: TypingIndicator, + args: { + avatarResolver, + usernames: [], + text: 'The attendant is typing', + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; + +export const WithAvatars = Template.bind({}); +WithAvatars.storyName = 'with avatars'; +WithAvatars.args = { + usernames: ['guilherme.gazzo', 'tasso.evangelista', 'martin.schoeler'], +}; diff --git a/packages/livechat/src/components/Messages/VideoAttachment/stories.js b/packages/livechat/src/components/Messages/VideoAttachment/stories.js deleted file mode 100644 index c13ae1ef7c41..000000000000 --- a/packages/livechat/src/components/Messages/VideoAttachment/stories.js +++ /dev/null @@ -1,11 +0,0 @@ -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import VideoAttachment from '.'; -import sampleVideo from '../../../../.storybook/assets/sample-video.mp4'; -import { centered } from '../../../helpers.stories'; - -storiesOf('Messages/VideoAttachment', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('default', () => ); diff --git a/packages/livechat/src/components/Messages/VideoAttachment/stories.tsx b/packages/livechat/src/components/Messages/VideoAttachment/stories.tsx new file mode 100644 index 000000000000..d6c54fe6ef74 --- /dev/null +++ b/packages/livechat/src/components/Messages/VideoAttachment/stories.tsx @@ -0,0 +1,21 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import VideoAttachment from '.'; +import { sampleVideo } from '../../../helpers.stories'; + +export default { + title: 'Messages/VideoAttachment', + component: VideoAttachment, + args: { + url: sampleVideo, + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Default = Template.bind({}); +Default.storyName = 'default'; diff --git a/packages/livechat/src/components/Messages/constants.js b/packages/livechat/src/components/Messages/constants.ts similarity index 100% rename from packages/livechat/src/components/Messages/constants.js rename to packages/livechat/src/components/Messages/constants.ts diff --git a/packages/livechat/src/components/Modal/stories.js b/packages/livechat/src/components/Modal/stories.js deleted file mode 100644 index 4bb52a71de96..000000000000 --- a/packages/livechat/src/components/Modal/stories.js +++ /dev/null @@ -1,75 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, number, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { loremIpsum, centered } from '../../helpers.stories'; -import Modal from './component'; - -const LoremIpsum = ({ padding = '5rem', count = 5, units = 'paragraphs', ...options }) => ( -
- {loremIpsum({ count, units, ...options }) - .split('\n') - .map((paragraph) => ( -

{paragraph}

- ))} -
-); - -storiesOf('Components/Modal', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('normal', () => ( -
- - - {text('content', loremIpsum({ count: 1, units: 'paragraphs' }))} - -
- )) - .add('animated', () => ( -
- - - {text('content', loremIpsum({ count: 1, units: 'paragraphs' }))} - -
- )) - .add('timeout', () => ( -
- - - {text('content', loremIpsum({ count: 1, units: 'paragraphs' }))} - -
- )) - .add('disallow dismiss by overlay', () => ( -
- - - {text('content', loremIpsum({ count: 1, units: 'paragraphs' }))} - -
- )) - .add('confirm', () => ( -
- - -
- )) - .add('alert', () => ( -
- - -
- )); diff --git a/packages/livechat/src/components/Modal/stories.tsx b/packages/livechat/src/components/Modal/stories.tsx new file mode 100644 index 000000000000..7af9aeb6605e --- /dev/null +++ b/packages/livechat/src/components/Modal/stories.tsx @@ -0,0 +1,83 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { loremIpsum } from '../../helpers.stories'; +import Modal from './component'; + +export default { + title: 'Components/Modal', + decorators: [ + (storyFn) => ( +
+
+ {loremIpsum({ count: 5, units: 'paragraphs' }) + .split('\n') + .map((paragraph, i) => ( +

{paragraph}

+ ))} +
+ {storyFn()} +
+ ), + ], + parameters: { + layout: 'centered', + }, +} satisfies Meta; + +export const Normal: Story> = (args) => ; +Normal.storyName = 'normal'; +Normal.args = { + children: loremIpsum({ count: 1, units: 'paragraphs' }), + open: true, + animated: false, + onDismiss: action('dismiss'), +}; + +export const Animated: Story> = (args) => ; +Animated.storyName = 'animated'; +Animated.args = { + children: loremIpsum({ count: 1, units: 'paragraphs' }), + open: true, + animated: true, + onDismiss: action('dismiss'), +}; + +export const Timeout: Story> = (args) => ; +Timeout.storyName = 'timeout'; +Timeout.args = { + children: loremIpsum({ count: 1, units: 'paragraphs' }), + open: true, + animated: false, + timeout: 3000, + onDismiss: action('dismiss'), +}; + +export const DisallowDismissByOverlay: Story> = (args) => ; +DisallowDismissByOverlay.storyName = 'disallow dismiss by overlay'; +DisallowDismissByOverlay.args = { + children: loremIpsum({ count: 1, units: 'paragraphs' }), + open: true, + animated: false, + dismissByOverlay: false, + onDismiss: action('dismiss'), +}; + +export const Confirm: Story> = (args) => ; +Confirm.storyName = 'confirm'; +Confirm.args = { + text: 'Are you ok?', + confirmButtonText: 'Yes', + cancelButtonText: 'No', + onConfirm: action('confirm'), + onCancel: action('cancel'), +}; + +export const Alert: Story> = (args) => ; +Alert.storyName = 'alert'; +Alert.args = { + text: 'You look great today.', + buttonText: 'OK', + onConfirm: action('confirm'), +}; diff --git a/packages/livechat/src/components/Popover/index.js b/packages/livechat/src/components/Popover/index.js index 55ed2147acc2..306afc87348a 100644 --- a/packages/livechat/src/components/Popover/index.js +++ b/packages/livechat/src/components/Popover/index.js @@ -83,6 +83,7 @@ export class PopoverContainer extends Component { ); } +/** @type {function({ children: [function({ pop: function() }), function({ dismiss: any, triggerBounds?: any })], overlayProps?: any }): any} */ export const PopoverTrigger = ({ children, ...props }) => ( {({ open }) => children[0]({ pop: open.bind(null, children[1], props) })} ); diff --git a/packages/livechat/src/components/Popover/stories.js b/packages/livechat/src/components/Popover/stories.js deleted file mode 100644 index f7c9ae875a0c..000000000000 --- a/packages/livechat/src/components/Popover/stories.js +++ /dev/null @@ -1,36 +0,0 @@ -import { withKnobs, object } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { PopoverContainer, PopoverTrigger } from '.'; -import { centered } from '../../helpers.stories'; -import { Button } from '../Button'; - -const centeredWithPopoverContainer = (storyFn, ...args) => ( -
- {centered(storyFn, ...args)} -
-); - -storiesOf('Components/Popover', module) - .addDecorator(withKnobs) - .addDecorator(centeredWithPopoverContainer) - .add('arbitrary renderer', () => ( - - {({ pop }) => } - {({ dismiss, triggerBounds = {} }) => ( - - )} - - )) - .add('with overlay props', () => ( - - {({ pop }) => } - {({ dismiss, triggerBounds = {} }) => ( - - )} - - )); diff --git a/packages/livechat/src/components/Popover/stories.tsx b/packages/livechat/src/components/Popover/stories.tsx new file mode 100644 index 000000000000..ecf87d9ddd97 --- /dev/null +++ b/packages/livechat/src/components/Popover/stories.tsx @@ -0,0 +1,44 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { PopoverContainer, PopoverTrigger } from '.'; +import { Button } from '../Button'; + +export default { + title: 'Components/Popover', + component: PopoverTrigger, + decorators: [ + (storyFn) => ( + +
{storyFn()}
+
+ ), + ], + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta>; + +export const ArbitraryRenderer: Story> = () => ( + + {({ pop }) => } + {({ dismiss, triggerBounds = {} }) => ( + + )} + +); +ArbitraryRenderer.storyName = 'arbitrary renderer'; + +export const WithOverlayProps: Story> = () => ( + + {({ pop }) => } + {({ dismiss, triggerBounds = {} }) => ( + + )} + +); +WithOverlayProps.storyName = 'with overlay props'; diff --git a/packages/livechat/src/components/Screen/Footer.stories.tsx b/packages/livechat/src/components/Screen/Footer.stories.tsx new file mode 100644 index 000000000000..5038b1faf584 --- /dev/null +++ b/packages/livechat/src/components/Screen/Footer.stories.tsx @@ -0,0 +1,67 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import i18next from 'i18next'; +import type { ComponentProps } from 'preact'; + +import { Screen } from '.'; +import { screenDecorator } from '../../helpers.stories'; +import { FooterOptions } from '../Footer'; +import Menu from '../Menu'; + +export default { + title: 'Components/Screen/Footer', + component: Screen.Footer, + decorators: [ + (storyFn) => ( + + + {storyFn()} + + ), + screenDecorator, + ], + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +export const Empty: Story> = () => ; +Empty.storyName = 'empty'; + +export const WithChildren: Story> = () => ( + Lorem ipsum dolor sit amet, his id atqui repudiare. +); +WithChildren.storyName = 'with children'; + +export const WithOptions: Story> = () => ( + + + {i18next.t('change_department')} + {i18next.t('forget_remove_my_data')} + + {i18next.t('finish_this_chat')} + + + + } + /> +); +WithOptions.storyName = 'with options'; diff --git a/packages/livechat/src/components/Screen/index.js b/packages/livechat/src/components/Screen/index.js index c7116b27349f..3a11000c43cb 100644 --- a/packages/livechat/src/components/Screen/index.js +++ b/packages/livechat/src/components/Screen/index.js @@ -72,6 +72,7 @@ const CssVar = ({ theme }) => { ); }; +/** @type {{ (props: any) => JSX.Element; Content: (props: any) => JSX.Element; Footer: (props: any) => JSX.Element }} */ export const Screen = ({ theme = {}, agent, diff --git a/packages/livechat/src/components/Screen/stories.js b/packages/livechat/src/components/Screen/stories.js deleted file mode 100644 index 3590b00c6870..000000000000 --- a/packages/livechat/src/components/Screen/stories.js +++ /dev/null @@ -1,321 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, color, object, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; -import i18next from 'i18next'; - -import { Screen } from '.'; -import { screenCentered, gazzoAvatar } from '../../helpers.stories'; -import { FooterOptions } from '../Footer'; -import Menu from '../Menu'; - -const alerts = [ - { id: 1, children: 'Success alert', success: true }, - { id: 2, children: 'Warning alert', warning: true, timeout: 0 }, - { id: 3, children: 'Error alert', error: true, timeout: 1000 }, - { id: 4, children: 'Custom colored alert', color: '#000', timeout: 5000 }, -]; - -storiesOf('Components/Screen', module) - .addDecorator(withKnobs) - .addDecorator(screenCentered) - .add('normal', () => ( - - {text('content', 'Content')} - - )) - .add('minimized', () => ( - - {text('content', 'Content')} - - )) - .add('expanded', () => ( - - {text('content', 'Content')} - - )) - .add('windowed', () => ( - - {text('content', 'Content')} - - )) - .add('with agent (email)', () => ( - - {text('content', 'Content')} - - )) - .add('with agent (phone)', () => ( - - {text('content', 'Content')} - - )) - .add('with agent', () => ( - - {text('content', 'Content')} - - )) - .add('with hidden agent', () => ( - - {text('content', 'Content')} - - )) - .add('with multiple alerts', () => ( - - {text('content', 'Content')} - - )); -storiesOf('Components/Screen/Footer', module) - .addDecorator(withKnobs) - .addDecorator(screenCentered) - .add('empty', () => ( - - - - - )) - .add('with children', () => ( - - - {text('content', 'Lorem ipsum dolor sit amet, his id atqui repudiare.')} - - )) - .add('with options', () => ( - - - - - {i18next.t('change_department')} - {i18next.t('forget_remove_my_data')} - - {i18next.t('finish_this_chat')} - - - - } - /> - - )); diff --git a/packages/livechat/src/components/Screen/stories.tsx b/packages/livechat/src/components/Screen/stories.tsx new file mode 100644 index 000000000000..5113046cc97a --- /dev/null +++ b/packages/livechat/src/components/Screen/stories.tsx @@ -0,0 +1,121 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { Screen } from '.'; +import { gazzoAvatar, screenDecorator } from '../../helpers.stories'; + +export default { + title: 'Components/Screen', + component: Screen, + args: { + theme: { + color: '', + fontColor: '', + iconColor: '', + }, + title: 'Title', + notificationsEnabled: true, + minimized: false, + expanded: false, + windowed: false, + onEnableNotifications: action('enableNotifications'), + onDisableNotifications: action('disableNotifications'), + onMinimize: action('minimize'), + onRestore: action('restore'), + onOpenWindow: action('openWindow'), + }, + decorators: [screenDecorator], + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ( + + Content + +); + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; + +export const Minimized = Template.bind({}); +Minimized.storyName = 'minimized'; +Minimized.args = { + minimized: true, +}; + +export const Expanded = Template.bind({}); +Expanded.storyName = 'expanded'; +Expanded.args = { + expanded: true, +}; + +export const Windowed = Template.bind({}); +Windowed.storyName = 'windowed'; +Windowed.args = { + windowed: true, +}; + +export const WithAgentEmail = Template.bind({}); +WithAgentEmail.storyName = 'with agent (email)'; +WithAgentEmail.args = { + agent: { + name: 'Guilherme Gazzo', + status: 'away', + email: 'guilherme.gazzo@rocket.chat', + avatar: { + description: 'guilherme.gazzo', + src: gazzoAvatar, + }, + }, +}; + +export const WithAgentPhone = Template.bind({}); +WithAgentPhone.storyName = 'with agent (phone)'; +WithAgentPhone.args = { + agent: { + name: 'Guilherme Gazzo', + status: 'away', + phone: '+ 55 42423 24242', + avatar: { + description: 'guilherme.gazzo', + src: gazzoAvatar, + }, + }, +}; + +export const WithAgentPhoneAndEmail = Template.bind({}); +WithAgentPhoneAndEmail.storyName = 'with agent (phone and email)'; +WithAgentPhoneAndEmail.args = { + agent: { + name: 'Guilherme Gazzo', + status: 'away', + email: 'guilherme.gazzo@rocket.chat', + phone: '+ 55 42423 24242', + avatar: { + description: 'guilherme.gazzo', + src: gazzoAvatar, + }, + }, +}; + +export const WithHiddenAgent = Template.bind({}); +WithHiddenAgent.storyName = 'with hidden agent'; +WithHiddenAgent.args = { + agent: { + hiddenInfo: true, + }, +}; + +export const WithMultipleAlerts = Template.bind({}); +WithMultipleAlerts.storyName = 'with multiple alerts'; +WithMultipleAlerts.args = { + alerts: [ + { id: 1, children: 'Success alert', success: true }, + { id: 2, children: 'Warning alert', warning: true, timeout: 0 }, + { id: 3, children: 'Error alert', error: true, timeout: 1000 }, + { id: 4, children: 'Custom colored alert', color: '#000', timeout: 5000 }, + ], +}; diff --git a/packages/livechat/src/components/Sound/stories.js b/packages/livechat/src/components/Sound/stories.js deleted file mode 100644 index 91c58950afbe..000000000000 --- a/packages/livechat/src/components/Sound/stories.js +++ /dev/null @@ -1,18 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { Sound } from '.'; -import beepAudio from '../../../.storybook/assets/beep.mp3'; -import sampleAudio from '../../../.storybook/assets/sample-audio.mp3'; -import { centered } from '../../helpers.stories'; - -storiesOf('Components/Sound', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('short', () => ( - - )) - .add('long', () => ( - - )); diff --git a/packages/livechat/src/components/Sound/stories.tsx b/packages/livechat/src/components/Sound/stories.tsx new file mode 100644 index 000000000000..4a5a89b16ab5 --- /dev/null +++ b/packages/livechat/src/components/Sound/stories.tsx @@ -0,0 +1,33 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { Sound } from '.'; +import { beepAudio, sampleAudio } from '../../helpers.stories'; + +export default { + title: 'Components/Sound', + component: Sound, + args: { + play: false, + onStart: action('start'), + onStop: action('stop'), + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +const Template: Story> = (args) => ; + +export const Short = Template.bind({}); +Short.storyName = 'short'; +Short.args = { + src: beepAudio, +}; + +export const Long = Template.bind({}); +Long.storyName = 'long'; +Long.args = { + src: sampleAudio, +}; diff --git a/packages/livechat/src/components/Tooltip/stories.js b/packages/livechat/src/components/Tooltip/stories.js deleted file mode 100644 index d2acf87b50fd..000000000000 --- a/packages/livechat/src/components/Tooltip/stories.js +++ /dev/null @@ -1,44 +0,0 @@ -import { withKnobs, boolean, select, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import Tooltip, { withTooltip } from '.'; -import { centered } from '../../helpers.stories'; -import { Button } from '../Button'; - -const tooltipText = 'A simple tool tip'; -const tooltipHidden = false; -const placements = [null, 'left', 'top', 'right', 'bottom', 'top-left', 'top-right', 'bottom-left', 'bottom-right']; - -storiesOf('Components/Tooltip', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('inline', () => ( - - )) - .add('placements', () => ( -
- {placements.map((placement) => ( - - ))} -
- )) - .add('connected to another component', () => ( - - - - - - )) - .add('withTooltip()', () => { - const MyButton = withTooltip(Button); - - return ( - - A simple button - - ); - }); diff --git a/packages/livechat/src/components/Tooltip/stories.tsx b/packages/livechat/src/components/Tooltip/stories.tsx new file mode 100644 index 000000000000..8c1f4f9790c3 --- /dev/null +++ b/packages/livechat/src/components/Tooltip/stories.tsx @@ -0,0 +1,59 @@ +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import Tooltip, { withTooltip } from '.'; +import { Button } from '../Button'; + +const placements = [null, 'left', 'top', 'right', 'bottom', 'top-left', 'top-right', 'bottom-left', 'bottom-right'] as const; + +export default { + title: 'Components/Tooltip', + component: Tooltip, + args: { + children: 'A simple tool tip', + hidden: false, + }, + argTypes: { + placement: { + control: { + type: 'select', + options: placements, + }, + }, + }, + parameters: { + layout: 'centered', + }, +} satisfies Meta>; + +export const Inline: Story> = (args) => ; +Inline.storyName = 'inline'; + +export const Placements: Story> = (args) => ( +
+ {placements.map((placement, i) => ( + + ))} +
+); +Placements.storyName = 'placements'; + +export const ConnectedToAnotherComponent: Story> = (args) => ( + + + +); +ConnectedToAnotherComponent.storyName = 'connected to another component'; +ConnectedToAnotherComponent.args = { + content: 'A simple tool tip', +}; +ConnectedToAnotherComponent.decorators = [(storyFn) => {storyFn()}]; + +const MyButton = withTooltip(Button); + +export const WithTooltip: Story<{ tooltip?: string }> = ({ tooltip }) => A simple button; +WithTooltip.storyName = 'withTooltip()'; +WithTooltip.args = { + tooltip: 'A simple tool tip', +}; +WithTooltip.decorators = [(storyFn) => {storyFn()}]; diff --git a/packages/livechat/src/components/uiKit/message/actions.stories.js b/packages/livechat/src/components/uiKit/message/ActionsBlock.stories.tsx similarity index 97% rename from packages/livechat/src/components/uiKit/message/actions.stories.js rename to packages/livechat/src/components/uiKit/message/ActionsBlock.stories.tsx index 482f807e1cd5..4892e9c4c926 100644 --- a/packages/livechat/src/components/uiKit/message/actions.stories.js +++ b/packages/livechat/src/components/uiKit/message/ActionsBlock.stories.tsx @@ -1,4 +1,5 @@ import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/preact'; import { renderMessageBlocks } from '.'; import Surface from './Surface'; @@ -13,14 +14,14 @@ export default { (storyFn) => ( { + dispatchAction={async (payload: unknown) => { await new Promise((resolve) => setTimeout(resolve, 1000)); action('dispatchAction')(payload); }} /> ), ], -}; +} as Meta; export const AllSelects = () => renderMessageBlocks([ diff --git a/packages/livechat/src/components/uiKit/message/ButtonElement/stories.js b/packages/livechat/src/components/uiKit/message/ButtonElement/stories.tsx similarity index 75% rename from packages/livechat/src/components/uiKit/message/ButtonElement/stories.js rename to packages/livechat/src/components/uiKit/message/ButtonElement/stories.tsx index 1f75aaae0cc6..adf60b56dc95 100644 --- a/packages/livechat/src/components/uiKit/message/ButtonElement/stories.js +++ b/packages/livechat/src/components/uiKit/message/ButtonElement/stories.tsx @@ -1,5 +1,7 @@ import { BlockContext } from '@rocket.chat/ui-kit'; import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; import ButtonElement from '.'; import { parser } from '..'; @@ -7,38 +9,38 @@ import Surface from '../Surface'; export default { title: 'UiKit/Message/Button element', - parameters: { - layout: 'centered', + args: { + text: { type: 'plain_text', text: 'Click Me' }, + actionId: undefined, + url: undefined, + value: undefined, + style: null, + context: undefined, + confirm: undefined, + }, + argTypes: { + text: { control: 'object' }, + actionId: { control: 'text' }, + url: { control: 'text' }, + value: { control: 'text' }, + style: { control: { type: 'inline-radio', options: [null, 'primary', 'danger'] } }, + context: { control: { type: 'select', options: BlockContext } }, }, decorators: [ (storyFn) => ( { + dispatchAction={async (payload: unknown) => { await new Promise((resolve) => setTimeout(resolve, 1000)); action('dispatchAction')(payload); }} /> ), ], - argTypes: { - text: { control: 'object' }, - actionId: { control: 'text' }, - url: { control: 'text' }, - value: { control: 'text' }, - style: { control: { type: 'inline-radio', options: [null, 'primary', 'danger'] } }, - context: { control: { type: 'select', options: BlockContext } }, - }, - args: { - text: { type: 'plain_text', text: 'Click Me' }, - actionId: undefined, - url: undefined, - value: undefined, - style: null, - context: undefined, - confirm: undefined, + parameters: { + layout: 'centered', }, -}; +} as Meta>; -export const Default = (args) => ; +export const Default: Story> = (args) => ; Default.storyName = 'default'; diff --git a/packages/livechat/src/components/uiKit/message/context.stories.js b/packages/livechat/src/components/uiKit/message/ContextBlock.stories.tsx similarity index 92% rename from packages/livechat/src/components/uiKit/message/context.stories.js rename to packages/livechat/src/components/uiKit/message/ContextBlock.stories.tsx index 2446351ac540..1760ca18135d 100644 --- a/packages/livechat/src/components/uiKit/message/context.stories.js +++ b/packages/livechat/src/components/uiKit/message/ContextBlock.stories.tsx @@ -1,5 +1,7 @@ +import type { Meta } from '@storybook/preact'; + import { renderMessageBlocks } from '.'; -import accessoryImage from '../../../../.storybook/assets/accessoryImage.png'; +import { accessoryImage } from '../../../helpers.stories'; export default { title: 'UiKit/Message/Context block', @@ -7,7 +9,7 @@ export default { layout: 'centered', }, decorators: [(storyFn) =>
], -}; +} as Meta; export const PlainText = () => renderMessageBlocks([ diff --git a/packages/livechat/src/components/uiKit/message/divider.stories.js b/packages/livechat/src/components/uiKit/message/DividerBlock.stories.tsx similarity index 85% rename from packages/livechat/src/components/uiKit/message/divider.stories.js rename to packages/livechat/src/components/uiKit/message/DividerBlock.stories.tsx index 0e01c4be1c2c..c5b9c39d70fc 100644 --- a/packages/livechat/src/components/uiKit/message/divider.stories.js +++ b/packages/livechat/src/components/uiKit/message/DividerBlock.stories.tsx @@ -1,3 +1,5 @@ +import type { Meta } from '@storybook/preact'; + import { renderMessageBlocks } from '.'; export default { @@ -6,7 +8,7 @@ export default { layout: 'centered', }, decorators: [(storyFn) =>
], -}; +} as Meta; export const Default = () => renderMessageBlocks([ diff --git a/packages/livechat/src/components/uiKit/message/image.stories.js b/packages/livechat/src/components/uiKit/message/ImageBlock.stories.tsx similarity index 85% rename from packages/livechat/src/components/uiKit/message/image.stories.js rename to packages/livechat/src/components/uiKit/message/ImageBlock.stories.tsx index 50681475bbb5..2623e606dbc9 100644 --- a/packages/livechat/src/components/uiKit/message/image.stories.js +++ b/packages/livechat/src/components/uiKit/message/ImageBlock.stories.tsx @@ -1,5 +1,7 @@ +import type { Meta } from '@storybook/preact'; + import { renderMessageBlocks } from '.'; -import imageBlock from '../../../../.storybook/assets/imageBlock.png'; +import { imageBlock } from '../../../helpers.stories'; export default { title: 'UiKit/Message/Image block', @@ -7,7 +9,7 @@ export default { layout: 'centered', }, decorators: [(storyFn) =>
], -}; +} satisfies Meta; export const WithTitle = () => renderMessageBlocks([ diff --git a/packages/livechat/src/components/uiKit/message/section.stories.js b/packages/livechat/src/components/uiKit/message/SectionBlock.stories.tsx similarity index 96% rename from packages/livechat/src/components/uiKit/message/section.stories.js rename to packages/livechat/src/components/uiKit/message/SectionBlock.stories.tsx index 5b6026286003..f24c308da090 100644 --- a/packages/livechat/src/components/uiKit/message/section.stories.js +++ b/packages/livechat/src/components/uiKit/message/SectionBlock.stories.tsx @@ -1,7 +1,8 @@ import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/preact'; import { renderMessageBlocks } from '.'; -import accessoryImage from '../../../../.storybook/assets/accessoryImage.png'; +import { accessoryImage } from '../../../helpers.stories'; import { PopoverContainer } from '../../Popover'; import Surface from './Surface'; @@ -16,14 +17,14 @@ export default { (storyFn) => ( { + dispatchAction={async (payload: unknown) => { await new Promise((resolve) => setTimeout(resolve, 1000)); action('dispatchAction')(payload); }} /> ), ], -}; +} as Meta; export const TextAsPlainText = () => renderMessageBlocks([ diff --git a/packages/livechat/src/components/uiKit/message/index.js b/packages/livechat/src/components/uiKit/message/index.tsx similarity index 75% rename from packages/livechat/src/components/uiKit/message/index.js rename to packages/livechat/src/components/uiKit/message/index.tsx index e7caf59d9ded..d5b417b1fb0d 100644 --- a/packages/livechat/src/components/uiKit/message/index.js +++ b/packages/livechat/src/components/uiKit/message/index.tsx @@ -13,8 +13,8 @@ import PlainText from './PlainText'; import SectionBlock from './SectionBlock'; import StaticSelectElement from './StaticSelectElement'; -class MessageParser extends UiKitParserMessage { - divider = (element, context, index) => { +class MessageParser extends UiKitParserMessage { + divider = (element: any, context: any, index: any) => { if (context !== BlockContext.BLOCK) { return null; } @@ -22,7 +22,7 @@ class MessageParser extends UiKitParserMessage { return ; }; - section = (element, context, index) => { + section = (element: any, context: any, index: any) => { if (context !== BlockContext.BLOCK) { return null; } @@ -30,7 +30,7 @@ class MessageParser extends UiKitParserMessage { return ; }; - image = (element, context, index) => { + image = (element: any, context: any, index: any) => { if (context === BlockContext.BLOCK) { return ; } @@ -38,7 +38,7 @@ class MessageParser extends UiKitParserMessage { return ; }; - actions = (element, context, index) => { + actions = (element: any, context: any, index: any) => { if (context !== BlockContext.BLOCK) { return null; } @@ -46,7 +46,7 @@ class MessageParser extends UiKitParserMessage { return ; }; - context = (element, context, index) => { + context = (element: any, context: any, index: any) => { if (context !== BlockContext.BLOCK) { return null; } @@ -54,7 +54,7 @@ class MessageParser extends UiKitParserMessage { return ; }; - plain_text = (element, context, index) => { + plain_text = (element: any, context: any, index: any) => { if (context === BlockContext.BLOCK) { return null; } @@ -62,7 +62,7 @@ class MessageParser extends UiKitParserMessage { return ; }; - mrkdwn = (element, context, index) => { + mrkdwn = (element: any, context: any, index: any) => { if (context === BlockContext.BLOCK) { return null; } @@ -70,7 +70,7 @@ class MessageParser extends UiKitParserMessage { return <Mrkdwn key={index} {...element} />; }; - button = (element, context, index) => { + button = (element: any, context: any, index: any) => { if (context === BlockContext.BLOCK) { return null; } @@ -78,7 +78,7 @@ class MessageParser extends UiKitParserMessage { return <ButtonElement key={index} {...element} parser={this} context={context} />; }; - overflow = (element, context, index) => { + overflow = (element: any, context: any, index: any) => { if (context === BlockContext.BLOCK) { return null; } @@ -86,7 +86,7 @@ class MessageParser extends UiKitParserMessage { return <OverflowElement key={index} {...element} parser={this} context={context} />; }; - datePicker = (element, context, index) => { + datePicker = (element: any, context: any, index: any) => { if (context === BlockContext.BLOCK) { return null; } @@ -94,7 +94,7 @@ class MessageParser extends UiKitParserMessage { return <DatePickerElement key={index} {...element} parser={this} context={context} />; }; - staticSelect = (element, context, index) => { + staticSelect = (element: any, context: any, index: any) => { if (context === BlockContext.BLOCK) { return null; } @@ -109,4 +109,4 @@ export const parser = new MessageParser(); export const renderMessageBlocks = uiKitMessage(parser, { engine: 'livechat', -}); +}) as { (blocks: any): JSX.Element[] }; diff --git a/packages/livechat/src/global.d.ts b/packages/livechat/src/global.d.ts index 79f8eda9cdb6..c19e42b90d1b 100644 --- a/packages/livechat/src/global.d.ts +++ b/packages/livechat/src/global.d.ts @@ -1,3 +1,32 @@ -interface Window { - SERVER_URL: string; +import type { ComponentType } from 'preact'; + +export {}; + +declare global { + interface Window { + SERVER_URL: string; + } + + namespace preact { + interface Component { + // This is a workaround for https://github.com/preactjs/preact/issues/1206 + refs: Record<string, any>; + } + + interface Provider<P> { + $$typeof: symbol; + props: P & { children?: ComponentChildren }; + } + + interface Consumer { + $$typeof: symbol; + } + } +} + +declare module '@storybook/preact/dist/ts3.9/client/preview/types-6-0.d.ts' { + export type PreactFramework = { + component: ComponentType<any, any>; + storyResult: StoryFnPreactReturnType; + }; } diff --git a/packages/livechat/src/helpers.stories.js b/packages/livechat/src/helpers.stories.js index 54a6c141e2e6..1a597d8a2e82 100644 --- a/packages/livechat/src/helpers.stories.js +++ b/packages/livechat/src/helpers.stories.js @@ -1,29 +1,21 @@ import { action } from '@storybook/addon-actions'; -import { boolean, color } from '@storybook/addon-knobs'; import { loremIpsum as originalLoremIpsum } from 'lorem-ipsum'; import gazzoAvatar from '../.storybook/assets/gazzo.jpg'; import martinAvatar from '../.storybook/assets/martin.jpg'; import tassoAvatar from '../.storybook/assets/tasso.jpg'; -export const centered = (storyFn) => ( - <div style={{ position: 'fixed', top: 0, left: 0, bottom: 0, right: 0, display: 'flex', alignItems: 'center', overflow: 'auto' }}> - <div style={{ margin: 'auto', maxHeight: '100%' }}>{storyFn()}</div> - </div> -); - -export const screenCentered = (storyFn, ...args) => - centered(() => <div style={{ display: 'flex', width: '365px', height: '500px' }}>{storyFn()}</div>, ...args); +export const screenDecorator = (storyFn) => <div style={{ display: 'flex', width: 365, height: 500 }}>{storyFn()}</div>; export const screenProps = () => ({ theme: { - color: color('theme.color', ''), - fontColor: color('theme.fontColor', ''), - iconColor: color('theme.iconColor', ''), + color: '', + fontColor: '', + iconColor: '', }, - notificationsEnabled: boolean('notificationsEnabled', true), - minimized: boolean('minimized', false), - windowed: boolean('windowed', false), + notificationsEnabled: true, + minimized: false, + windowed: false, onEnableNotifications: action('enableNotifications'), onDisableNotifications: action('disableNotifications'), onMinimize: action('minimize'), @@ -48,3 +40,10 @@ const loremIpsumRandom = createRandom(42); export const loremIpsum = (options) => originalLoremIpsum({ random: loremIpsumRandom, ...options }); export { gazzoAvatar, martinAvatar, tassoAvatar }; + +export { default as sampleAudio } from '../.storybook/assets/sample-audio.mp3'; +export { default as sampleImage } from '../.storybook/assets/sample-image.jpg'; +export { default as sampleVideo } from '../.storybook/assets/sample-video.mp4'; +export { default as accessoryImage } from '../.storybook/assets/accessoryImage.png'; +export { default as imageBlock } from '../.storybook/assets/imageBlock.png'; +export { default as beepAudio } from '../.storybook/assets/beep.mp3'; diff --git a/packages/livechat/src/icons/stories.js b/packages/livechat/src/icons/stories.js deleted file mode 100644 index 970ecb23fd4a..000000000000 --- a/packages/livechat/src/icons/stories.js +++ /dev/null @@ -1,52 +0,0 @@ -import path from 'path'; - -import { withKnobs, color } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { centered } from '../helpers.stories'; - -const req = require.context('./', true, /\.svg$/); -const iconset = req.keys().map((filename) => ({ - component: req(filename), - name: path.basename(filename, '.svg'), -})); - -const IconDisplay = ({ component: Icon, name, color }) => ( - <div - style={{ - width: '130px', - height: '130px', - margin: '10px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'stretch', - }} - > - <div style={{ flex: '1', display: 'flex', alignItems: 'center', color }}> - <Icon width={48} height={48} /> - </div> - <div style={{ flex: '0' }}>{name}</div> - </div> -); - -storiesOf('Components/Icons', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add('all', () => ( - <div style={{ width: '100%', display: 'flex', flexWrap: 'wrap' }}> - {iconset.map((props) => ( - <IconDisplay color={color('color', '#E0364D')} {...props} /> - ))} - </div> - )); -iconset.forEach(({ component: Icon, name }) => - storiesOf('Components/Icons', module) - .addDecorator(centered) - .addDecorator(withKnobs) - .add(name, () => ( - <div style={{ color: color('color', '#E0364D') }}> - <Icon width={256} height={256} /> - </div> - )), -); diff --git a/packages/livechat/src/icons/stories.tsx b/packages/livechat/src/icons/stories.tsx new file mode 100644 index 000000000000..2351e50f39af --- /dev/null +++ b/packages/livechat/src/icons/stories.tsx @@ -0,0 +1,57 @@ +import path from 'path'; + +import type { Meta, Story } from '@storybook/preact'; + +export default { + title: 'Components/Icons', + argTypes: { + color: { + control: 'color', + defaultValue: '#E0364D', + }, + }, + decorators: [(storyFn) => <div style={{ width: '100%', display: 'flex', flexWrap: 'wrap' }}>{storyFn()}</div>], + parameters: { + layout: 'centered', + }, +} satisfies Meta<{ color: string }>; + +const req = require.context('./', true, /\.svg$/); +const iconset = req.keys().map((filename) => ({ + component: req(filename), + name: path.basename(filename, '.svg'), +})); + +export const All: Story<{ color: string }> = ({ color }) => ( + <> + {iconset.map( + ( + { + // eslint-disable-next-line @typescript-eslint/naming-convention + component: Icon, + name, + }, + i, + ) => ( + <div + key={i} + style={{ + width: 130, + height: 130, + margin: 10, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'stretch', + }} + > + <div style={{ flex: 1, display: 'flex', alignItems: 'center', color }}> + <Icon width={48} height={48} /> + </div> + <div style={{ flex: 0 }}>{name}</div> + </div> + ), + )} + </> +); +All.storyName = 'all'; diff --git a/packages/livechat/src/routes/Chat/stories.js b/packages/livechat/src/routes/Chat/stories.js deleted file mode 100644 index 2ed307d65c36..000000000000 --- a/packages/livechat/src/routes/Chat/stories.js +++ /dev/null @@ -1,131 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, number, object, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import soundSrc from '../../../.storybook/assets/beep.mp3'; -import { screenCentered, screenProps, avatarResolver } from '../../helpers.stories'; -import Chat from './component'; - -const agent = { - name: 'Guilherme Gazzo', - status: 'online', - email: 'guilherme.gazzo@rocket.chat', - phone: '+55 99 99999 9999', - username: 'guilherme.gazzo', -}; - -const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); - -const messages = [ - { - _id: 1, - u: { _id: 1, username: 'tasso.evangelista' }, - msg: 'Lorem ipsum dolor sit amet, ea usu quod eirmod lucilius, mea veri viris concludaturque id, vel eripuit fabulas ea', - }, - { _id: 2, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Putent appareat te sea, dico recusabo pri te' }, - { _id: 3, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Iudico utinam volutpat eos eu, sadipscing repudiandae pro te' }, - { _id: 4, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Movet doming ad ius, mel id adversarium disputationi' }, - { _id: 5, u: { _id: 1, username: 'tasso.evangelista' }, msg: 'Adhuc latine et nec' }, - { _id: 6, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Vis at verterem adversarium concludaturque' }, - { _id: 7, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Sea no congue scripta persecuti, sed amet fabulas voluptaria ex' }, - { _id: 8, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Invidunt repudiandae has eu' }, - { _id: 9, u: { _id: 1, username: 'tasso.evangelista' }, msg: 'Veri soluta suscipit mel no' }, -]; - -const triggers = [ - { _id: 1, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Putent appareat te sea, dico recusabo pri te' }, - { _id: 2, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Iudico utinam volutpat eos eu, sadipscing repudiandae pro te' }, -]; - -const normalizeMessages = (messages = []) => - messages.map((message, i) => ({ - ...message, - ts: new Date(now.getTime() - (15 - i) * 60000 - (i < 5 ? 24 * 60 * 60 * 1000 : 0)).toISOString(), - })); - -storiesOf('Routes/Chat', module) - .addDecorator(screenCentered) - .addDecorator(withKnobs) - .add('loading', () => ( - <Chat - title={text('title', '')} - sound={{ src: soundSrc, play: false }} - avatarResolver={avatarResolver} - uid={number('uid', 1)} - agent={object('agent', agent)} - messages={object('messages', [])} - typingUsernames={object('typingUsernames', [])} - emoji={boolean('emoji', false)} - uploads={boolean('uploads', false)} - loading={boolean('loading', true)} - onTop={action('top')} - onBottom={action('bottom')} - onUpload={action('upload')} - onSubmit={action('submit')} - limitTextLength={number('limitTextLength', 0)} - {...screenProps()} - /> - )) - .add('normal', () => ( - <Chat - title={text('title', '')} - sound={{ src: soundSrc, play: false }} - avatarResolver={avatarResolver} - uid={number('uid', 1)} - agent={object('agent', agent)} - messages={object('messages', normalizeMessages(messages))} - typingUsernames={object('typingUsernames', [])} - emoji={boolean('emoji', false)} - uploads={boolean('uploads', false)} - loading={boolean('loading', false)} - lastReadMessageId={number('lastReadMessageId', 8)} - onTop={action('top')} - onBottom={action('bottom')} - onUpload={action('upload')} - onSubmit={action('submit')} - limitTextLength={number('limitTextLength', 0)} - {...screenProps()} - /> - )) - .add('with typing user', () => ( - <Chat - title={text('title', '')} - sound={{ src: soundSrc, play: false }} - avatarResolver={avatarResolver} - uid={number('uid', 1)} - agent={object('agent', agent)} - messages={object('messages', normalizeMessages(messages))} - typingUsernames={object('typingUsernames', ['guilherme.gazzo'])} - emoji={boolean('emoji', false)} - uploads={boolean('uploads', false)} - loading={boolean('loading', false)} - lastReadMessageId={number('lastReadMessageId', 8)} - onTop={action('top')} - onBottom={action('bottom')} - onUpload={action('upload')} - onSubmit={action('submit')} - limitTextLength={number('limitTextLength', 0)} - {...screenProps()} - /> - )) - .add('with trigger messages', () => ( - <Chat - title={text('title', '')} - sound={{ src: soundSrc, play: false }} - avatarResolver={avatarResolver} - uid={number('uid', 1)} - agent={object('agent', agent)} - messages={object('messages', normalizeMessages(triggers))} - typingUsernames={object('typingUsernames', [])} - emoji={boolean('emoji', false)} - uploads={boolean('uploads', false)} - loading={boolean('loading', false)} - lastReadMessageId={number('lastReadMessageId', 8)} - onTop={action('top')} - onBottom={action('bottom')} - onUpload={action('upload')} - onSubmit={action('submit')} - registrationRequired={boolean('registrationRequired', true)} - {...screenProps()} - /> - )); diff --git a/packages/livechat/src/routes/Chat/stories.tsx b/packages/livechat/src/routes/Chat/stories.tsx new file mode 100644 index 000000000000..f2aa1a4f6c80 --- /dev/null +++ b/packages/livechat/src/routes/Chat/stories.tsx @@ -0,0 +1,91 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { screenProps, avatarResolver, beepAudio, screenDecorator } from '../../helpers.stories'; +import Chat from './component'; + +const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); + +const normalizeMessages = (messages: any[] = []) => + messages.map((message, i) => ({ + ...message, + ts: new Date(now.getTime() - (15 - i) * 60000 - (i < 5 ? 24 * 60 * 60 * 1000 : 0)).toISOString(), + })); + +export default { + title: 'Routes/Chat', + component: Chat, + args: { + title: '', + sound: { src: beepAudio, play: false }, + avatarResolver, + uid: 1, + agent: { + name: 'Guilherme Gazzo', + status: 'online', + email: 'guilherme.gazzo@rocket.chat', + phone: '+55 99 99999 9999', + username: 'guilherme.gazzo', + }, + messages: normalizeMessages([ + { + _id: 1, + u: { _id: 1, username: 'tasso.evangelista' }, + msg: 'Lorem ipsum dolor sit amet, ea usu quod eirmod lucilius, mea veri viris concludaturque id, vel eripuit fabulas ea', + }, + { _id: 2, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Putent appareat te sea, dico recusabo pri te' }, + { _id: 3, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Iudico utinam volutpat eos eu, sadipscing repudiandae pro te' }, + { _id: 4, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Movet doming ad ius, mel id adversarium disputationi' }, + { _id: 5, u: { _id: 1, username: 'tasso.evangelista' }, msg: 'Adhuc latine et nec' }, + { _id: 6, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Vis at verterem adversarium concludaturque' }, + { _id: 7, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Sea no congue scripta persecuti, sed amet fabulas voluptaria ex' }, + { _id: 8, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Invidunt repudiandae has eu' }, + { _id: 9, u: { _id: 1, username: 'tasso.evangelista' }, msg: 'Veri soluta suscipit mel no' }, + ]), + typingUsernames: [], + emoji: false, + uploads: false, + loading: false, + limitTextLength: 0, + registrationRequired: false, + onTop: action('top'), + onBottom: action('bottom'), + onUpload: action('upload'), + onSubmit: action('submit'), + ...screenProps(), + }, + decorators: [screenDecorator], + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta<ComponentProps<typeof Chat>>; + +const Template: Story<ComponentProps<typeof Chat>> = (args) => <Chat {...args} />; + +export const Loading = Template.bind({}); +Loading.storyName = 'loading'; +Loading.args = { + messages: [], + loading: true, +}; + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; + +export const WithTypingUser = Template.bind({}); +WithTypingUser.storyName = 'with typing user'; +WithTypingUser.args = { + typingUsernames: ['guilherme.gazzo'], +}; + +export const WithTriggerMessages = Template.bind({}); +WithTriggerMessages.storyName = 'with trigger messages'; +WithTriggerMessages.args = { + messages: normalizeMessages([ + { _id: 1, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Putent appareat te sea, dico recusabo pri te' }, + { _id: 2, u: { _id: 2, username: 'guilherme.gazzo' }, msg: 'Iudico utinam volutpat eos eu, sadipscing repudiandae pro te' }, + ]), + lastReadMessageId: 8, + registrationRequired: true, +}; diff --git a/packages/livechat/src/routes/ChatFinished/stories.js b/packages/livechat/src/routes/ChatFinished/stories.js deleted file mode 100644 index 6764f490c0d8..000000000000 --- a/packages/livechat/src/routes/ChatFinished/stories.js +++ /dev/null @@ -1,31 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { screenCentered, screenProps, loremIpsum } from '../../helpers.stories'; -import ChatFinished from './component'; - -const customGreeting = loremIpsum({ count: 3, units: 'words' }); -const customText = loremIpsum({ count: 2, units: 'sentences' }); - -storiesOf('Routes/ChatFinished', module) - .addDecorator(screenCentered) - .addDecorator(withKnobs) - .add('normal', () => ( - <ChatFinished - title={text('title', 'Chat Finished')} - greeting={text('greeting', '')} - message={text('message', '')} - onRedirectChat={action('redirectChat')} - {...screenProps()} - /> - )) - .add('with custom messages', () => ( - <ChatFinished - title={text('title', 'Chat Finished')} - greeting={text('greeting', customGreeting)} - message={text('message', customText)} - onRedirectChat={action('redirectChat')} - {...screenProps()} - /> - )); diff --git a/packages/livechat/src/routes/ChatFinished/stories.tsx b/packages/livechat/src/routes/ChatFinished/stories.tsx new file mode 100644 index 000000000000..4479ee57d99d --- /dev/null +++ b/packages/livechat/src/routes/ChatFinished/stories.tsx @@ -0,0 +1,34 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { screenProps, loremIpsum, screenDecorator } from '../../helpers.stories'; +import ChatFinished from './component'; + +export default { + title: 'Routes/ChatFinished', + component: ChatFinished, + args: { + title: 'Chat Finished', + greeting: '', + message: '', + onRedirectChat: action('redirectChat'), + ...screenProps(), + }, + decorators: [screenDecorator], + parameters: { + layout: 'centered', + }, +} satisfies Meta<ComponentProps<typeof ChatFinished>>; + +const Template: Story<ComponentProps<typeof ChatFinished>> = (args) => <ChatFinished {...args} />; + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; + +export const WithCustomMessages = Template.bind({}); +WithCustomMessages.storyName = 'with custom messages'; +WithCustomMessages.args = { + greeting: loremIpsum({ count: 3, units: 'words' }), + message: loremIpsum({ count: 2, units: 'sentences' }), +}; diff --git a/packages/livechat/src/routes/GDPRAgreement/stories.js b/packages/livechat/src/routes/GDPRAgreement/stories.js deleted file mode 100644 index 7b5fa2834738..000000000000 --- a/packages/livechat/src/routes/GDPRAgreement/stories.js +++ /dev/null @@ -1,19 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { screenCentered, screenProps } from '../../helpers.stories'; -import GDPRAgreement from './component'; - -storiesOf('Routes/GDPRAgreement', module) - .addDecorator(screenCentered) - .addDecorator(withKnobs) - .add('normal', () => ( - <GDPRAgreement - title={text('title', 'GDPR')} - consentText={text('consentText', '')} - instructions={text('instructions', '')} - onAgree={action('agree')} - {...screenProps()} - /> - )); diff --git a/packages/livechat/src/routes/GDPRAgreement/stories.tsx b/packages/livechat/src/routes/GDPRAgreement/stories.tsx new file mode 100644 index 000000000000..cc764b888441 --- /dev/null +++ b/packages/livechat/src/routes/GDPRAgreement/stories.tsx @@ -0,0 +1,27 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { screenDecorator, screenProps } from '../../helpers.stories'; +import GDPRAgreement from './component'; + +export default { + title: 'Routes/GDPRAgreement', + component: GDPRAgreement, + args: { + title: 'GDPR', + consentText: '', + instructions: '', + onAgree: action('agree'), + ...screenProps(), + }, + decorators: [screenDecorator], + parameters: { + layout: 'centered', + }, +} satisfies Meta<ComponentProps<typeof GDPRAgreement>>; + +const Template: Story<ComponentProps<typeof GDPRAgreement>> = (args) => <GDPRAgreement {...args} />; + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; diff --git a/packages/livechat/src/routes/LeaveMessage/stories.js b/packages/livechat/src/routes/LeaveMessage/stories.js deleted file mode 100644 index 14fcd9320049..000000000000 --- a/packages/livechat/src/routes/LeaveMessage/stories.js +++ /dev/null @@ -1,43 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { screenCentered, screenProps } from '../../helpers.stories'; -import LeaveMessage from './component'; - -storiesOf('Routes/Leave a message', module) - .addDecorator(screenCentered) - .addDecorator(withKnobs) - .add('normal', () => ( - <LeaveMessage - title={text('title', '')} - message={text('message', '')} - unavailableMessage={text('unavailableMessage', '')} - hasForm={boolean('hasForm', true)} - loading={boolean('loading', false)} - onSubmit={action('submit')} - {...screenProps()} - /> - )) - .add('loading', () => ( - <LeaveMessage - title={text('title', '')} - message={text('message', '')} - unavailableMessage={text('unavailableMessage', '')} - hasForm={boolean('hasForm', true)} - loading={boolean('loading', true)} - onSubmit={action('submit')} - {...screenProps()} - /> - )) - .add('unavailable', () => ( - <LeaveMessage - title={text('title', '')} - message={text('message', '')} - unavailableMessage={text('unavailableMessage', '')} - hasForm={boolean('hasForm', false)} - loading={boolean('loading', false)} - onSubmit={action('submit')} - {...screenProps()} - /> - )); diff --git a/packages/livechat/src/routes/LeaveMessage/stories.tsx b/packages/livechat/src/routes/LeaveMessage/stories.tsx new file mode 100644 index 000000000000..58f683459ca2 --- /dev/null +++ b/packages/livechat/src/routes/LeaveMessage/stories.tsx @@ -0,0 +1,42 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { screenDecorator, screenProps } from '../../helpers.stories'; +import LeaveMessage from './component'; + +export default { + title: 'Routes/Leave a message', + component: LeaveMessage, + args: { + title: '', + message: '', + unavailableMessage: '', + hasForm: true, + loading: false, + onSubmit: action('submit'), + ...screenProps(), + }, + decorators: [screenDecorator], + parameters: { + layout: 'centered', + }, +} satisfies Meta<ComponentProps<typeof LeaveMessage>>; + +const Template: Story<ComponentProps<typeof LeaveMessage>> = (args) => <LeaveMessage {...args} />; + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; + +export const Loading = Template.bind({}); +Loading.storyName = 'loading'; +Loading.args = { + loading: true, +}; + +export const Unavailable = Template.bind({}); +Unavailable.storyName = 'unavailable'; +Unavailable.args = { + hasForm: false, + unavailableMessage: 'Sorry, we are not available at the moment. Please leave a message.', +}; diff --git a/packages/livechat/src/routes/Register/stories.js b/packages/livechat/src/routes/Register/stories.js deleted file mode 100644 index 014d0cfeb4da..000000000000 --- a/packages/livechat/src/routes/Register/stories.js +++ /dev/null @@ -1,106 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, object, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { screenCentered, screenProps } from '../../helpers.stories'; -import Register from './component'; - -const customFields = [ - { - _id: 'website', - label: 'Website', - type: 'input', - required: true, - }, - { - _id: 'area', - label: 'Area', - type: 'select', - defaultValue: 'Marketing', - options: ['Human Resources', 'Sales', 'Marketing', 'Supply', 'Development'], - required: true, - }, -]; - -storiesOf('Routes/Register', module) - .addDecorator(screenCentered) - .addDecorator(withKnobs) - .add('normal', () => ( - <Register - title={text('title', '')} - message={text('message', '')} - hasNameField={boolean('hasNameField', true)} - hasEmailField={boolean('hasEmailField', true)} - hasDepartmentField={boolean('hasDepartmentField', true)} - departments={object('departments', [ - { - _id: 1, - name: 'Department #1', - }, - { - _id: 2, - name: 'Department #2', - }, - { - _id: 3, - name: 'Department #3', - }, - ])} - loading={boolean('loading', false)} - onSubmit={action('submit')} - {...screenProps()} - /> - )) - .add('loading', () => ( - <Register - title={text('title', '')} - message={text('message', '')} - hasNameField={boolean('hasNameField', true)} - hasEmailField={boolean('hasEmailField', true)} - hasDepartmentField={boolean('hasDepartmentField', true)} - departments={object('departments', [ - { - _id: 1, - name: 'Department #1', - }, - { - _id: 2, - name: 'Department #2', - }, - { - _id: 3, - name: 'Department #3', - }, - ])} - loading={boolean('loading', true)} - onSubmit={action('submit')} - {...screenProps()} - /> - )) - .add('with custom fields', () => ( - <Register - title={text('title', '')} - message={text('message', '')} - hasNameField={boolean('hasNameField', true)} - hasEmailField={boolean('hasEmailField', true)} - hasDepartmentField={boolean('hasDepartmentField', true)} - departments={object('departments', [ - { - _id: 1, - name: 'Department #1', - }, - { - _id: 2, - name: 'Department #2', - }, - { - _id: 3, - name: 'Department #3', - }, - ])} - loading={boolean('loading', false)} - onSubmit={action('submit')} - customFields={customFields} - {...screenProps()} - /> - )); diff --git a/packages/livechat/src/routes/Register/stories.tsx b/packages/livechat/src/routes/Register/stories.tsx new file mode 100644 index 000000000000..26a77d090e63 --- /dev/null +++ b/packages/livechat/src/routes/Register/stories.tsx @@ -0,0 +1,71 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { screenDecorator, screenProps } from '../../helpers.stories'; +import Register from './component'; + +export default { + title: 'Routes/Register', + component: Register, + args: { + title: '', + message: '', + hasNameField: true, + hasEmailField: true, + hasDepartmentField: true, + departments: [ + { + _id: 1, + name: 'Department #1', + }, + { + _id: 2, + name: 'Department #2', + }, + { + _id: 3, + name: 'Department #3', + }, + ], + loading: false, + onSubmit: action('submit'), + ...screenProps(), + }, + decorators: [screenDecorator], + parameters: { + layout: 'centered', + }, +} satisfies Meta<ComponentProps<typeof Register>>; + +const Template: Story<ComponentProps<typeof Register>> = (args) => <Register {...args} />; + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; + +export const Loading = Template.bind({}); +Loading.storyName = 'loading'; +Loading.args = { + loading: true, +}; + +export const WithCustomFields = Template.bind({}); +WithCustomFields.storyName = 'with custom fields'; +WithCustomFields.args = { + customFields: [ + { + _id: 'website', + label: 'Website', + type: 'input', + required: true, + }, + { + _id: 'area', + label: 'Area', + type: 'select', + defaultValue: 'Marketing', + options: ['Human Resources', 'Sales', 'Marketing', 'Supply', 'Development'], + required: true, + }, + ], +}; diff --git a/packages/livechat/src/routes/SwitchDepartment/stories.js b/packages/livechat/src/routes/SwitchDepartment/stories.js deleted file mode 100644 index 03584ad0d808..000000000000 --- a/packages/livechat/src/routes/SwitchDepartment/stories.js +++ /dev/null @@ -1,63 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, color, object, text } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { screenCentered, screenProps } from '../../helpers.stories'; -import SwitchDepartment from './component'; - -storiesOf('Routes/SwitchDepartment', module) - .addDecorator(screenCentered) - .addDecorator(withKnobs) - .add('normal', () => ( - <SwitchDepartment - theme={{ - color: color('theme/color', ''), - fontColor: color('theme/fontColor', ''), - iconColor: color('theme/iconColor', ''), - }} - title={text('title', '')} - message={text('message', '')} - departments={object('departments', [ - { - _id: 1, - name: 'Department #1', - }, - { - _id: 2, - name: 'Department #2', - }, - { - _id: 3, - name: 'Department #3', - }, - ])} - loading={boolean('loading', false)} - onSubmit={action('submit')} - onCancel={action('cancel')} - {...screenProps()} - /> - )) - .add('loading', () => ( - <SwitchDepartment - title={text('title', '')} - message={text('message', '')} - departments={object('departments', [ - { - _id: 1, - name: 'Department #1', - }, - { - _id: 2, - name: 'Department #2', - }, - { - _id: 3, - name: 'Department #3', - }, - ])} - loading={boolean('loading', true)} - onSubmit={action('submit')} - onCancel={action('cancel')} - {...screenProps()} - /> - )); diff --git a/packages/livechat/src/routes/SwitchDepartment/stories.tsx b/packages/livechat/src/routes/SwitchDepartment/stories.tsx new file mode 100644 index 000000000000..def96290b315 --- /dev/null +++ b/packages/livechat/src/routes/SwitchDepartment/stories.tsx @@ -0,0 +1,48 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { screenDecorator, screenProps } from '../../helpers.stories'; +import SwitchDepartment from './component'; + +export default { + title: 'Routes/SwitchDepartment', + component: SwitchDepartment, + args: { + title: '', + message: '', + departments: [ + { + _id: 1, + name: 'Department #1', + }, + { + _id: 2, + name: 'Department #2', + }, + { + _id: 3, + name: 'Department #3', + }, + ], + loading: false, + onSubmit: action('submit'), + onCancel: action('cancel'), + ...screenProps(), + }, + decorators: [screenDecorator], + parameters: { + layout: 'centered', + }, +} as Meta<ComponentProps<typeof SwitchDepartment>>; + +const Template: Story<ComponentProps<typeof SwitchDepartment>> = (args) => <SwitchDepartment {...args} />; + +export const Normal = Template.bind({}); +Normal.storyName = 'normal'; + +export const Loading = Template.bind({}); +Loading.storyName = 'loading'; +Loading.args = { + loading: true, +}; diff --git a/packages/livechat/src/routes/TriggerMessage/stories.js b/packages/livechat/src/routes/TriggerMessage/stories.js deleted file mode 100644 index 0fe12f21096f..000000000000 --- a/packages/livechat/src/routes/TriggerMessage/stories.js +++ /dev/null @@ -1,56 +0,0 @@ -import { action } from '@storybook/addon-actions'; -import { withKnobs, color, text, object } from '@storybook/addon-knobs'; -import { storiesOf } from '@storybook/react'; - -import { screenCentered, screenProps } from '../../helpers.stories'; -import TriggerMessage from './component'; - -const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); - -const messages = [ - { _id: 1, u: { _id: 1, username: 'guilherme.gazzo' }, msg: 'Hi There!' }, - { - _id: 2, - u: { _id: 2, username: 'guilherme.gazzo' }, - msg: 'Rocket.Chat allows you to chat and create better relationship with your customers on their favorite channels. ', - }, - { _id: 3, u: { _id: 3, username: 'guilherme.gazzo' }, msg: 'Let us know if you have any question.' }, -].map((message, i) => ({ - ...message, - ts: new Date(now.getTime() - (15 - i) * 60000 - (i < 5 ? 24 * 60 * 60 * 1000 : 0)).toISOString(), -})); - -storiesOf('Routes/TriggerMessage', module) - .addDecorator(screenCentered) - .addDecorator(withKnobs) - .add('single', () => ( - <TriggerMessage - theme={{ - color: color('theme/color', ''), - fontColor: color('theme/fontColor', ''), - iconColor: color('theme/iconColor', ''), - }} - messages={object( - 'messages', - messages.filter(({ _id }) => _id === 3), - )} - title={text('title', '')} - onSubmit={action('submit')} - onCancel={action('cancel')} - {...screenProps()} - /> - )) - .add('multiple', () => ( - <TriggerMessage - theme={{ - color: color('theme/color', ''), - fontColor: color('theme/fontColor', ''), - iconColor: color('theme/iconColor', ''), - }} - messages={object('messages', messages)} - title={text('title', '')} - onSubmit={action('submit')} - onCancel={action('cancel')} - {...screenProps()} - /> - )); diff --git a/packages/livechat/src/routes/TriggerMessage/stories.tsx b/packages/livechat/src/routes/TriggerMessage/stories.tsx new file mode 100644 index 000000000000..b530461a9d51 --- /dev/null +++ b/packages/livechat/src/routes/TriggerMessage/stories.tsx @@ -0,0 +1,48 @@ +import { action } from '@storybook/addon-actions'; +import type { Meta, Story } from '@storybook/preact'; +import type { ComponentProps } from 'preact'; + +import { screenDecorator, screenProps } from '../../helpers.stories'; +import TriggerMessage from './component'; + +const now = new Date(Date.parse('2021-01-01T00:00:00.000Z')); + +const messages = [ + { _id: 1, u: { _id: 1, username: 'guilherme.gazzo' }, msg: 'Hi There!' }, + { + _id: 2, + u: { _id: 2, username: 'guilherme.gazzo' }, + msg: 'Rocket.Chat allows you to chat and create better relationship with your customers on their favorite channels. ', + }, + { _id: 3, u: { _id: 3, username: 'guilherme.gazzo' }, msg: 'Let us know if you have any question.' }, +].map((message, i) => ({ + ...message, + ts: new Date(now.getTime() - (15 - i) * 60000 - (i < 5 ? 24 * 60 * 60 * 1000 : 0)).toISOString(), +})); + +export default { + title: 'Routes/TriggerMessage', + component: TriggerMessage, + args: { + messages, + title: '', + onSubmit: action('submit'), + onCancel: action('cancel'), + ...screenProps(), + }, + decorators: [screenDecorator], + parameters: { + layout: 'centered', + }, +} satisfies Meta<ComponentProps<typeof TriggerMessage>>; + +const Template: Story<ComponentProps<typeof TriggerMessage>> = (args) => <TriggerMessage {...args} />; + +export const Single = Template.bind({}); +Single.storyName = 'single'; +Single.args = { + messages: messages.slice(-1), +}; + +export const Multiple = Template.bind({}); +Multiple.storyName = 'multiple'; diff --git a/yarn.lock b/yarn.lock index 0b573475ee3c..210c2672084b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1453,7 +1453,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.21.4": +"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.21.4": version: 7.21.4 resolution: "@babel/helper-module-imports@npm:7.21.4" dependencies: @@ -3864,7 +3864,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.21.0 resolution: "@babel/runtime@npm:7.21.0" dependencies: @@ -4656,52 +4656,6 @@ __metadata: languageName: node linkType: hard -"@emotion/cache@npm:^10.0.27, @emotion/cache@npm:^10.0.9": - version: 10.0.29 - resolution: "@emotion/cache@npm:10.0.29" - dependencies: - "@emotion/sheet": 0.9.4 - "@emotion/stylis": 0.8.5 - "@emotion/utils": 0.11.3 - "@emotion/weak-memoize": 0.2.5 - checksum: 78b37fb0c2e513c90143a927abef229e995b6738ef8a92ce17abe2ed409b38859ddda7c14d7f4854d6f4e450b6db50231532f53a7fec4903d7ae775b2ae3fd64 - languageName: node - linkType: hard - -"@emotion/core@npm:^10.0.9": - version: 10.3.1 - resolution: "@emotion/core@npm:10.3.1" - dependencies: - "@babel/runtime": ^7.5.5 - "@emotion/cache": ^10.0.27 - "@emotion/css": ^10.0.27 - "@emotion/serialize": ^0.11.15 - "@emotion/sheet": 0.9.4 - "@emotion/utils": 0.11.3 - peerDependencies: - react: ">=16.3.0" - checksum: d2dad428e1b2cf0777badfb55e262d369273be9b2e6e9e7d61c953066c00811d544a6234db36b17ee07872ed092f4dd102bf6ffe2c76fc38d53eef3a60fddfd0 - languageName: node - linkType: hard - -"@emotion/css@npm:^10.0.27, @emotion/css@npm:^10.0.9": - version: 10.0.27 - resolution: "@emotion/css@npm:10.0.27" - dependencies: - "@emotion/serialize": ^0.11.15 - "@emotion/utils": 0.11.3 - babel-plugin-emotion: ^10.0.27 - checksum: 1420f5b514fc3a8500bcf90384b309b0d9acc9f687ec3a655166b55dc81d1661d6b6132ea6fe6730d0071c10da93bf9427937c22a90a18088af4ba5e11d59141 - languageName: node - linkType: hard - -"@emotion/hash@npm:0.8.0": - version: 0.8.0 - resolution: "@emotion/hash@npm:0.8.0" - checksum: 4b35d88a97e67275c1d990c96d3b0450451d089d1508619488fc0acb882cb1ac91e93246d471346ebd1b5402215941ef4162efe5b51534859b39d8b3a0e3ffaa - languageName: node - linkType: hard - "@emotion/hash@npm:^0.9.0": version: 0.9.0 resolution: "@emotion/hash@npm:0.9.0" @@ -4709,61 +4663,6 @@ __metadata: languageName: node linkType: hard -"@emotion/memoize@npm:0.7.4": - version: 0.7.4 - resolution: "@emotion/memoize@npm:0.7.4" - checksum: 4e3920d4ec95995657a37beb43d3f4b7d89fed6caa2b173a4c04d10482d089d5c3ea50bbc96618d918b020f26ed6e9c4026bbd45433566576c1f7b056c3271dc - languageName: node - linkType: hard - -"@emotion/serialize@npm:^0.11.15, @emotion/serialize@npm:^0.11.16": - version: 0.11.16 - resolution: "@emotion/serialize@npm:0.11.16" - dependencies: - "@emotion/hash": 0.8.0 - "@emotion/memoize": 0.7.4 - "@emotion/unitless": 0.7.5 - "@emotion/utils": 0.11.3 - csstype: ^2.5.7 - checksum: 2949832fab9d803e6236f2af6aad021c09c6b6722ae910b06b4ec3bfb84d77cbecfe3eab9a7dcc269ac73e672ef4b696c7836825931670cb110731712e331438 - languageName: node - linkType: hard - -"@emotion/sheet@npm:0.9.4": - version: 0.9.4 - resolution: "@emotion/sheet@npm:0.9.4" - checksum: 53bb833b4bb69ea2af04e1ecad164f78fb2614834d2820f584c909686a8e047c44e96a6e824798c5c558e6d95e10772454a9e5c473c5dbe0d198e50deb2815bc - languageName: node - linkType: hard - -"@emotion/stylis@npm:0.8.5": - version: 0.8.5 - resolution: "@emotion/stylis@npm:0.8.5" - checksum: 67ff5958449b2374b329fb96e83cb9025775ffe1e79153b499537c6c8b2eb64b77f32d7b5d004d646973662356ceb646afd9269001b97c54439fceea3203ce65 - languageName: node - linkType: hard - -"@emotion/unitless@npm:0.7.5": - version: 0.7.5 - resolution: "@emotion/unitless@npm:0.7.5" - checksum: f976e5345b53fae9414a7b2e7a949aa6b52f8bdbcc84458b1ddc0729e77ba1d1dfdff9960e0da60183877873d3a631fa24d9695dd714ed94bcd3ba5196586a6b - languageName: node - linkType: hard - -"@emotion/utils@npm:0.11.3": - version: 0.11.3 - resolution: "@emotion/utils@npm:0.11.3" - checksum: 9c4204bda84f9acd153a9be9478a83f9baa74d5d7a4c21882681c4d1b86cd113b84540cb1f92e1c30313b5075f024da2658dbc553f5b00776ef9b6ec7991c0c9 - languageName: node - linkType: hard - -"@emotion/weak-memoize@npm:0.2.5": - version: 0.2.5 - resolution: "@emotion/weak-memoize@npm:0.2.5" - checksum: 27d402b0c683b94658220b6d47840346ee582329ca2a15ec9c233492e0f1a27687ccb233b76eedc922f2e185e444cc89f7b97a81a1d3e5ae9f075bab08e965ea - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.17.18": version: 0.17.18 resolution: "@esbuild/android-arm64@npm:0.17.18" @@ -9955,25 +9854,21 @@ __metadata: dependencies: "@babel/eslint-parser": ~7.22.5 "@babel/preset-env": ~7.22.5 + "@babel/preset-typescript": ~7.22.5 "@rocket.chat/ddp-client": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/fuselage-tokens": next "@rocket.chat/logo": next "@rocket.chat/sdk": ^1.0.0-alpha.42 "@rocket.chat/ui-kit": next - "@storybook/addon-actions": ~6.5.16 - "@storybook/addon-backgrounds": ~6.5.16 "@storybook/addon-essentials": ~6.5.16 - "@storybook/addon-knobs": ~6.4.0 "@storybook/addon-postcss": ~2.0.0 - "@storybook/addon-viewport": ~6.5.16 - "@storybook/react": ~6.5.16 + "@storybook/preact": ~6.5.16 "@storybook/theming": ~6.5.16 "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 autoprefixer: ^9.8.8 babel-loader: ^8.3.0 - babel-plugin-jsx-pragmatic: ^1.0.2 cross-env: ^7.0.3 crypto-js: ^4.1.1 css-loader: ^4.3.0 @@ -10008,7 +9903,7 @@ __metadata: postcss-logical: ^4.0.2 postcss-scss: ^4.0.6 postcss-selector-not: ^4.0.1 - preact: ^10.8.2 + preact: 10.15.1 preact-router: ^3.2.1 query-string: ^7.1.3 react-dom: ~17.0.2 @@ -11350,7 +11245,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-backgrounds@npm:6.5.16, @storybook/addon-backgrounds@npm:~6.5.16": +"@storybook/addon-backgrounds@npm:6.5.16": version: 6.5.16 resolution: "@storybook/addon-backgrounds@npm:6.5.16" dependencies: @@ -11545,38 +11440,6 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-knobs@npm:~6.4.0": - version: 6.4.0 - resolution: "@storybook/addon-knobs@npm:6.4.0" - dependencies: - copy-to-clipboard: ^3.3.1 - core-js: ^3.8.2 - escape-html: ^1.0.3 - fast-deep-equal: ^3.1.3 - global: ^4.4.0 - lodash: ^4.17.20 - prop-types: ^15.7.2 - qs: ^6.10.0 - react-colorful: ^5.1.2 - react-lifecycles-compat: ^3.0.4 - react-select: ^3.2.0 - peerDependencies: - "@storybook/addons": ^6.4.0 - "@storybook/api": ^6.4.0 - "@storybook/components": ^6.4.0 - "@storybook/core-events": ^6.4.0 - "@storybook/theming": ^6.4.0 - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - checksum: d9ea65af55e3983e7150aa0492f99027db06ca267eae5be19ace88f9733d1e858a35ba6cd0ff324f42fc32c03665ebf39e0f04777ec3b715300a2504de81a493 - languageName: node - linkType: hard - "@storybook/addon-links@npm:~6.5.16": version: 6.5.16 resolution: "@storybook/addon-links@npm:6.5.16" @@ -11691,7 +11554,7 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-viewport@npm:6.5.16, @storybook/addon-viewport@npm:~6.5.16": +"@storybook/addon-viewport@npm:6.5.16": version: 6.5.16 resolution: "@storybook/addon-viewport@npm:6.5.16" dependencies: @@ -12381,6 +12244,37 @@ __metadata: languageName: node linkType: hard +"@storybook/preact@npm:~6.5.16": + version: 6.5.16 + resolution: "@storybook/preact@npm:6.5.16" + dependencies: + "@babel/plugin-transform-react-jsx": ^7.12.12 + "@storybook/addons": 6.5.16 + "@storybook/core": 6.5.16 + "@storybook/core-common": 6.5.16 + "@storybook/csf": 0.0.2--canary.4566f4d.1 + "@storybook/store": 6.5.16 + "@types/node": ^14.14.20 || ^16.0.0 + "@types/webpack-env": ^1.16.0 + core-js: ^3.8.2 + global: ^4.4.0 + react: 16.14.0 + react-dom: 16.14.0 + read-pkg-up: ^7.0.1 + regenerator-runtime: ^0.13.7 + ts-dedent: ^2.0.0 + webpack: ">=4.0.0 <6.0.0" + peerDependencies: + "@babel/core": "*" + preact: ^8.0.0||^10.0.0 + bin: + build-storybook: bin/build.js + start-storybook: bin/index.js + storybook-server: bin/index.js + checksum: ddad4337b2a05ff84522f16d51402f0d10956b2084aca69169cf9dedf0c19643a449da642bbb29fcf83c9571ada2a56da6da8d90208bd17a86e5123284bd51f5 + languageName: node + linkType: hard + "@storybook/preview-web@npm:6.5.16": version: 6.5.16 resolution: "@storybook/preview-web@npm:6.5.16" @@ -16725,24 +16619,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-emotion@npm:^10.0.27": - version: 10.2.2 - resolution: "babel-plugin-emotion@npm:10.2.2" - dependencies: - "@babel/helper-module-imports": ^7.0.0 - "@emotion/hash": 0.8.0 - "@emotion/memoize": 0.7.4 - "@emotion/serialize": ^0.11.16 - babel-plugin-macros: ^2.0.0 - babel-plugin-syntax-jsx: ^6.18.0 - convert-source-map: ^1.5.0 - escape-string-regexp: ^1.0.5 - find-root: ^1.1.0 - source-map: ^0.5.7 - checksum: 763f38c67ffbe7d091691d68c74686ba478296cc24716699fb5b0feddce1b1b47878a20b0bbe2aa4dea17f41074ead4deae7935d2cf6823638766709812c5b40 - languageName: node - linkType: hard - "babel-plugin-extract-import-names@npm:1.6.22": version: 1.6.22 resolution: "babel-plugin-extract-import-names@npm:1.6.22" @@ -16777,26 +16653,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-jsx-pragmatic@npm:^1.0.2": - version: 1.0.2 - resolution: "babel-plugin-jsx-pragmatic@npm:1.0.2" - dependencies: - babel-plugin-syntax-jsx: ^6.0.0 - checksum: dbb5e5c07d8ca8525c3023b32fd93cabaa9a452dcd3446e2612074c93210a9e1163657c97436719e80705c1bf3e5ac4d453d8fd25051ce03bfb2b027e1a397cf - languageName: node - linkType: hard - -"babel-plugin-macros@npm:^2.0.0": - version: 2.8.0 - resolution: "babel-plugin-macros@npm:2.8.0" - dependencies: - "@babel/runtime": ^7.7.2 - cosmiconfig: ^6.0.0 - resolve: ^1.12.0 - checksum: 59b09a21cf3ae1e14186c1b021917d004b49b953824b24953a54c6502da79e8051d4ac31cfd4a0ae7f6ea5ddf1f7edd93df4895dd3c3982a5b2431859c2889ac - languageName: node - linkType: hard - "babel-plugin-macros@npm:^3.0.1": version: 3.1.0 resolution: "babel-plugin-macros@npm:3.1.0" @@ -16910,13 +16766,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-syntax-jsx@npm:^6.0.0, babel-plugin-syntax-jsx@npm:^6.18.0": - version: 6.18.0 - resolution: "babel-plugin-syntax-jsx@npm:6.18.0" - checksum: 0c7ce5b81d6cfc01a7dd7a76a9a8f090ee02ba5c890310f51217ef1a7e6163fb7848994bbc14fd560117892e82240df9c7157ad0764da67ca5f2afafb73a7d27 - languageName: node - linkType: hard - "babel-polyfill@npm:^6.2.0": version: 6.26.0 resolution: "babel-polyfill@npm:6.26.0" @@ -19311,7 +19160,7 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:^1.4.0, convert-source-map@npm:^1.5.0, convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0": +"convert-source-map@npm:^1.4.0, convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0": version: 1.8.0 resolution: "convert-source-map@npm:1.8.0" dependencies: @@ -19402,15 +19251,6 @@ __metadata: languageName: node linkType: hard -"copy-to-clipboard@npm:^3.3.1": - version: 3.3.1 - resolution: "copy-to-clipboard@npm:3.3.1" - dependencies: - toggle-selection: ^1.0.6 - checksum: 3c7b1c333dc6a4b2e9905f52e4df6bbd34ff9f9c97ecd3ca55378a6bc1c191bb12a3252e6289c7b436e9188cff0360d393c0161626851d2301607860bbbdcfd5 - languageName: node - linkType: hard - "core-js-compat@npm:^3.25.1, core-js-compat@npm:^3.8.1": version: 3.25.1 resolution: "core-js-compat@npm:3.25.1" @@ -20068,13 +19908,6 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^2.5.7": - version: 2.6.20 - resolution: "csstype@npm:2.6.20" - checksum: cb5d5ded49c3390909e93b20b285d4a63d0ba5b10294bdfbc4cf911f80e91d6cf367ea671f99f09570762535c14ea7074a2c7fa73f02008203f01328dea8968b - languageName: node - linkType: hard - "csstype@npm:^3.0.2": version: 3.0.11 resolution: "csstype@npm:3.0.11" @@ -21016,16 +20849,6 @@ __metadata: languageName: node linkType: hard -"dom-helpers@npm:^5.0.1": - version: 5.2.1 - resolution: "dom-helpers@npm:5.2.1" - dependencies: - "@babel/runtime": ^7.8.7 - csstype: ^3.0.2 - checksum: 863ba9e086f7093df3376b43e74ce4422571d404fc9828bf2c56140963d5edf0e56160f9b2f3bb61b282c07f8fc8134f023c98fd684bddcb12daf7b0f14d951c - languageName: node - linkType: hard - "dom-serializer@npm:0": version: 0.2.2 resolution: "dom-serializer@npm:0.2.2" @@ -23383,13 +23206,6 @@ __metadata: languageName: node linkType: hard -"find-root@npm:^1.1.0": - version: 1.1.0 - resolution: "find-root@npm:1.1.0" - checksum: b2a59fe4b6c932eef36c45a048ae8f93c85640212ebe8363164814990ee20f154197505965f3f4f102efc33bfb1cbc26fd17c4a2fc739ebc51b886b137cbefaf - languageName: node - linkType: hard - "find-up@npm:5.0.0, find-up@npm:^5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" @@ -29611,7 +29427,7 @@ __metadata: languageName: node linkType: hard -"memoize-one@npm:^5.0.0, memoize-one@npm:^5.1.1": +"memoize-one@npm:^5.1.1": version: 5.2.1 resolution: "memoize-one@npm:5.2.1" checksum: a3cba7b824ebcf24cdfcd234aa7f86f3ad6394b8d9be4c96ff756dafb8b51c7f71320785fbc2304f1af48a0467cbbd2a409efc9333025700ed523f254cb52e3d @@ -33808,10 +33624,17 @@ __metadata: languageName: node linkType: hard -"preact@npm:^10.8.2": - version: 10.8.2 - resolution: "preact@npm:10.8.2" - checksum: 183358ba03b4c104c89b383ea926099b49acd0435f24e95dee9ff0e81350d1bfbd77908f6a2ed16b3654d75e84832fd5b4479216c85fe8af6ad8c2ebe795c4f9 +"preact@npm:10.15.1": + version: 10.15.1 + resolution: "preact@npm:10.15.1" + checksum: dabad11843b19b40b11846a25ff0b1fc4bc58268909e01a7faddb341a40983d24fbe7d44ad6e2c5d35e43091963af616800552fec9af44dd0a2f0f698d1bba1f + languageName: node + linkType: hard + +"preact@patch:preact@npm:10.15.1#.yarn/patches/preact-npm-10.15.1-bd458de913.patch::locator=rocket.chat%40workspace%3A.": + version: 10.15.1 + resolution: "preact@patch:preact@npm%3A10.15.1#.yarn/patches/preact-npm-10.15.1-bd458de913.patch::version=10.15.1&hash=6e6d0e&locator=rocket.chat%40workspace%3A." + checksum: 6258efa196625543b88cb02ca1fe4eb3e2867be82bc58209cd8f0dce5aefc3a7da0df4ca440c26731fed9085c780e8b6b265183dd2a16ad66216d8a6480868b7 languageName: node linkType: hard @@ -34150,7 +33973,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.0.0, prop-types@npm:^15.5.4, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.0.0, prop-types@npm:^15.5.4, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -34784,16 +34607,6 @@ __metadata: languageName: node linkType: hard -"react-colorful@npm:^5.1.2": - version: 5.5.1 - resolution: "react-colorful@npm:5.5.1" - peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - checksum: e60811781716e57f0990379eff20d6f22d4d35b9e858c47ecf857c1dc1c1a2274c924ded7248bad5f1e2fbf2aab06e59b12852910c8dee5e6850f8e4df293670 - languageName: node - linkType: hard - "react-docgen-typescript-plugin@npm:^1.0.5, react-docgen-typescript-plugin@npm:~1.0.5": version: 1.0.5 resolution: "react-docgen-typescript-plugin@npm:1.0.5" @@ -34841,6 +34654,20 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:16.14.0": + version: 16.14.0 + resolution: "react-dom@npm:16.14.0" + dependencies: + loose-envify: ^1.1.0 + object-assign: ^4.1.1 + prop-types: ^15.6.2 + scheduler: ^0.19.1 + peerDependencies: + react: ^16.14.0 + checksum: 5a5c49da0f106b2655a69f96c622c347febcd10532db391c262b26aec225b235357d9da1834103457683482ab1b229af7a50f6927a6b70e53150275e31785544 + languageName: node + linkType: hard + "react-dom@npm:^17.0.2, react-dom@npm:~17.0.2": version: 17.0.2 resolution: "react-dom@npm:17.0.2" @@ -34927,17 +34754,6 @@ __metadata: languageName: node linkType: hard -"react-input-autosize@npm:^3.0.0": - version: 3.0.0 - resolution: "react-input-autosize@npm:3.0.0" - dependencies: - prop-types: ^15.5.8 - peerDependencies: - react: ^16.3.0 || ^17.0.0 - checksum: cc3309ddc87446ade742c7d0e88ef089dd8b6981f21506a2bb27daf01a8803ac697f64157c4ffc7e81dfcf3892b54a4072dbc3652fd9addcf6d22dd0b87ab723 - languageName: node - linkType: hard - "react-inspector@npm:^5.1.0": version: 5.1.1 resolution: "react-inspector@npm:5.1.1" @@ -35056,25 +34872,6 @@ __metadata: languageName: node linkType: hard -"react-select@npm:^3.2.0": - version: 3.2.0 - resolution: "react-select@npm:3.2.0" - dependencies: - "@babel/runtime": ^7.4.4 - "@emotion/cache": ^10.0.9 - "@emotion/core": ^10.0.9 - "@emotion/css": ^10.0.9 - memoize-one: ^5.0.0 - prop-types: ^15.6.0 - react-input-autosize: ^3.0.0 - react-transition-group: ^4.3.0 - peerDependencies: - react: ^16.8.0 || ^17.0.0 - react-dom: ^16.8.0 || ^17.0.0 - checksum: 082c818369fb8c7ce50bbd51260b21794f58dd41e9df5e0798c10e10478fb44b9fd88247f24720d5b443d77a6ec8afa733ecdd15a7722fde85c27bb87c379962 - languageName: node - linkType: hard - "react-split-pane@npm:^0.1.92": version: 0.1.92 resolution: "react-split-pane@npm:0.1.92" @@ -35129,21 +34926,6 @@ __metadata: languageName: node linkType: hard -"react-transition-group@npm:^4.3.0": - version: 4.4.2 - resolution: "react-transition-group@npm:4.4.2" - dependencies: - "@babel/runtime": ^7.5.5 - dom-helpers: ^5.0.1 - loose-envify: ^1.4.0 - prop-types: ^15.6.2 - peerDependencies: - react: ">=16.6.0" - react-dom: ">=16.6.0" - checksum: b67bf5b3e86dbab72d658b9a52a3589e5960583ab28c7c66272427d8fe30d4c7de422d5046ae96bd2683cdf80cc3264b2516f5ce80cae1dbe6cf3ca6dda392c5 - languageName: node - linkType: hard - "react-virtuoso@npm:^1.11.1": version: 1.11.1 resolution: "react-virtuoso@npm:1.11.1" @@ -35167,6 +34949,17 @@ __metadata: languageName: node linkType: hard +"react@npm:16.14.0": + version: 16.14.0 + resolution: "react@npm:16.14.0" + dependencies: + loose-envify: ^1.1.0 + object-assign: ^4.1.1 + prop-types: ^15.6.2 + checksum: 8484f3ecb13414526f2a7412190575fc134da785c02695eb92bb6028c930bfe1c238d7be2a125088fec663cc7cda0a3623373c46807cf2c281f49c34b79881ac + languageName: node + linkType: hard + "react@npm:^17.0.2, react@npm:~17.0.2": version: 17.0.2 resolution: "react@npm:17.0.2" @@ -36469,6 +36262,16 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.19.1": + version: 0.19.1 + resolution: "scheduler@npm:0.19.1" + dependencies: + loose-envify: ^1.1.0 + object-assign: ^4.1.1 + checksum: 73e185a59e2ff5aa3609f5b9cb97ddd376f89e1610579d29939d952411ca6eb7a24907a4ea4556569dacb931467a1a4a56d94fe809ef713aa76748642cd96a6c + languageName: node + linkType: hard + "scheduler@npm:^0.20.2": version: 0.20.2 resolution: "scheduler@npm:0.20.2" @@ -37366,7 +37169,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.5.0, source-map@npm:^0.5.6, source-map@npm:^0.5.7": +"source-map@npm:^0.5.0, source-map@npm:^0.5.6": version: 0.5.7 resolution: "source-map@npm:0.5.7" checksum: 5dc2043b93d2f194142c7f38f74a24670cd7a0063acdaf4bf01d2964b402257ae843c2a8fa822ad5b71013b5fcafa55af7421383da919752f22ff488bc553f4d @@ -39144,13 +38947,6 @@ __metadata: languageName: node linkType: hard -"toggle-selection@npm:^1.0.6": - version: 1.0.6 - resolution: "toggle-selection@npm:1.0.6" - checksum: a90dc80ed1e7b18db8f4e16e86a5574f87632dc729cfc07d9ea3ced50021ad42bb4e08f22c0913e0b98e3837b0b717e0a51613c65f30418e21eb99da6556a74c - languageName: node - linkType: hard - "toidentifier@npm:1.0.1": version: 1.0.1 resolution: "toidentifier@npm:1.0.1" @@ -41318,6 +41114,43 @@ __metadata: languageName: node linkType: hard +"webpack@npm:>=4.0.0 <6.0.0": + version: 5.88.0 + resolution: "webpack@npm:5.88.0" + dependencies: + "@types/eslint-scope": ^3.7.3 + "@types/estree": ^1.0.0 + "@webassemblyjs/ast": ^1.11.5 + "@webassemblyjs/wasm-edit": ^1.11.5 + "@webassemblyjs/wasm-parser": ^1.11.5 + acorn: ^8.7.1 + acorn-import-assertions: ^1.9.0 + browserslist: ^4.14.5 + chrome-trace-event: ^1.0.2 + enhanced-resolve: ^5.15.0 + es-module-lexer: ^1.2.1 + eslint-scope: 5.1.1 + events: ^3.2.0 + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.2.9 + json-parse-even-better-errors: ^2.3.1 + loader-runner: ^4.2.0 + mime-types: ^2.1.27 + neo-async: ^2.6.2 + schema-utils: ^3.2.0 + tapable: ^2.1.1 + terser-webpack-plugin: ^5.3.7 + watchpack: ^2.4.0 + webpack-sources: ^3.2.3 + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: 9fd1568b34ec2e99ba97c8509a15ab2576ec80c396e7015551ec814b24cfc11de173acba3e114dafe95f1a6d460781b09d6201e6a1fb15110e1d01a09f61a283 + languageName: node + linkType: hard + "webpack@npm:>=4.43.0 <6.0.0": version: 5.74.0 resolution: "webpack@npm:5.74.0" From 9ed8d45a667a0c27557dda7e923e4abb9058d6a1 Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Tue, 27 Jun 2023 11:05:47 -0600 Subject: [PATCH 016/149] chore: Add tests to business hours and remove logs (#29626) --- .../business-hour/AbstractBusinessHour.ts | 1 - .../business-hour/BusinessHourManager.ts | 1 + .../server/functions/getDefaultUserFields.ts | 1 + .../app/livechat-enterprise/client/startup.ts | 6 +- .../tests/data/livechat/business-hours.ts | 5 +- .../tests/data/livechat/businessHours.ts | 18 +++ apps/meteor/tests/data/livechat/rooms.ts | 11 +- .../api/livechat/19-business-hours.ts | 120 +++++++++++++++++- 8 files changed, 149 insertions(+), 14 deletions(-) diff --git a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts index 1c43aaf97a44..7c0ddd007d15 100644 --- a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts +++ b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts @@ -73,7 +73,6 @@ export abstract class AbstractBusinessHourType { } private convertWorkHours(businessHourData: ILivechatBusinessHour): ILivechatBusinessHour { - console.log('convertWorkHours', businessHourData); businessHourData.workHours.forEach((hour: any) => { const startUtc = moment.tz(`${hour.day}:${hour.start}`, 'dddd:HH:mm', businessHourData.timezone.name).utc(); const finishUtc = moment.tz(`${hour.day}:${hour.finish}`, 'dddd:HH:mm', businessHourData.timezone.name).utc(); diff --git a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts index ddb80126efec..b128fa64485e 100644 --- a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts +++ b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts @@ -132,6 +132,7 @@ export class BusinessHourManager { } const { start, finish } = workHours; + await Promise.all(start.map(({ day, times }) => this.scheduleCronJob(times, day, 'open', this.openWorkHoursCallback))); await Promise.all(finish.map(({ day, times }) => this.scheduleCronJob(times, day, 'close', this.closeWorkHoursCallback))); } diff --git a/apps/meteor/app/utils/server/functions/getDefaultUserFields.ts b/apps/meteor/app/utils/server/functions/getDefaultUserFields.ts index c5c939cfcaa5..03d0cae77ab9 100644 --- a/apps/meteor/app/utils/server/functions/getDefaultUserFields.ts +++ b/apps/meteor/app/utils/server/functions/getDefaultUserFields.ts @@ -35,4 +35,5 @@ export const getDefaultUserFields = (): DefaultUserFields => ({ '_updatedAt': 1, 'avatarETag': 1, 'extension': 1, + 'openBusinessHours': 1, }); diff --git a/apps/meteor/ee/app/livechat-enterprise/client/startup.ts b/apps/meteor/ee/app/livechat-enterprise/client/startup.ts index 8b4f71549862..b97823adf25e 100644 --- a/apps/meteor/ee/app/livechat-enterprise/client/startup.ts +++ b/apps/meteor/ee/app/livechat-enterprise/client/startup.ts @@ -8,14 +8,14 @@ import { EESingleBusinessHourBehaviour } from './SingleBusinessHour'; import { hasLicense } from '../../license/client'; const businessHours: Record<string, IBusinessHourBehavior> = { - Multiple: new MultipleBusinessHoursBehavior(), - Single: new EESingleBusinessHourBehaviour(), + multiple: new MultipleBusinessHoursBehavior(), + single: new EESingleBusinessHourBehaviour(), }; Meteor.startup(function () { settings.onload('Livechat_business_hour_type', async (_, value) => { if (await hasLicense('livechat-enterprise')) { - businessHourManager.registerBusinessHourBehavior(businessHours[value as string]); + businessHourManager.registerBusinessHourBehavior(businessHours[(value as string).toLowerCase()]); } }); }); diff --git a/apps/meteor/tests/data/livechat/business-hours.ts b/apps/meteor/tests/data/livechat/business-hours.ts index 28177c63056b..f3335047cca3 100644 --- a/apps/meteor/tests/data/livechat/business-hours.ts +++ b/apps/meteor/tests/data/livechat/business-hours.ts @@ -2,10 +2,11 @@ import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; import { credentials, methodCall, request } from '../api-data'; export const saveBusinessHour = async (businessHour: ILivechatBusinessHour) => { - await request + const { body } = await request .post(methodCall('livechat:saveBusinessHour')) .set(credentials) .send({ message: JSON.stringify({ params: [businessHour], msg: 'method', method: 'livechat:saveBusinessHour', id: '101' }) }) .expect(200); - return true; + + return JSON.parse(body.message); }; diff --git a/apps/meteor/tests/data/livechat/businessHours.ts b/apps/meteor/tests/data/livechat/businessHours.ts index ec254a88fc81..59b232a38d22 100644 --- a/apps/meteor/tests/data/livechat/businessHours.ts +++ b/apps/meteor/tests/data/livechat/businessHours.ts @@ -1,5 +1,8 @@ +import { ILivechatBusinessHour } from "@rocket.chat/core-typings"; import { api, credentials, methodCall, request } from "../api-data"; import { updateEESetting, updateSetting } from "../permissions.helper" +import moment from "moment"; +type ISaveBhApiWorkHour = Omit<ILivechatBusinessHour, '_id' | 'ts' | 'timezone'> & { workHours: { day: string, start: string, finish: string, open: boolean }[] } & { departmentsToApplyBusinessHour?: string } & { timezoneName: string }; export const makeDefaultBusinessHourActiveAndClosed = async () => { // enable settings @@ -72,3 +75,18 @@ export const disableDefaultBusinessHour = async () => { }), }); } + +export const getWorkHours = (open = true): ISaveBhApiWorkHour['workHours'] => { + const workHours: ISaveBhApiWorkHour['workHours'] = []; + + for (let i = 0; i < 7; i++) { + workHours.push({ + day: moment().day(i).format('dddd'), + start: '00:00', + finish: '23:59', + open, + }); + } + + return workHours; +} diff --git a/apps/meteor/tests/data/livechat/rooms.ts b/apps/meteor/tests/data/livechat/rooms.ts index 70c731b69ea8..bceedec83b1c 100644 --- a/apps/meteor/tests/data/livechat/rooms.ts +++ b/apps/meteor/tests/data/livechat/rooms.ts @@ -12,6 +12,7 @@ import { getSettingValueById, updatePermission, updateSetting } from '../permiss import { IUserCredentialsHeader, adminUsername } from '../user'; import { getRandomVisitorToken } from './users'; import { DummyResponse, sleep } from './utils'; +import { Response } from 'supertest'; export const createLivechatRoom = async (visitorToken: string, extraRoomParams?: Record<string, string>): Promise<IOmnichannelRoom> => { const urlParams = new URLSearchParams(); @@ -153,7 +154,7 @@ export const createManager = (overrideUsername?: string): Promise<ILivechatAgent }); }); -export const makeAgentAvailable = async (overrideCredentials?: { 'X-Auth-Token': string; 'X-User-Id': string }): Promise<void> => { +export const makeAgentAvailable = async (overrideCredentials?: { 'X-Auth-Token': string | undefined; 'X-User-Id': string | undefined }): Promise<Response> => { await updatePermission('view-l-room', ['livechat-agent', 'livechat-manager', 'admin']); await request .post(api('users.setStatus')) @@ -161,16 +162,14 @@ export const makeAgentAvailable = async (overrideCredentials?: { 'X-Auth-Token': .send({ message: '', status: 'online', - }) - .expect(200); + }); - await request + return request .post(api('livechat/agent.status')) .set(overrideCredentials || credentials) .send({ status: 'available', - }) - .expect(200); + }); }; export const makeAgentUnavailable = async (overrideCredentials?: { 'X-Auth-Token': string; 'X-User-Id': string }): Promise<void> => { 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 5342f47f5ea5..eb5393920c2d 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 @@ -1,12 +1,14 @@ /* eslint-env mocha */ -import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; +import { LivechatBusinessHourTypes, LivechatBusinessHourBehaviors } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { getCredentials, api, request, credentials } from '../../../data/api-data'; import { saveBusinessHour } from '../../../data/livechat/business-hours'; import { updatePermission, updateSetting } from '../../../data/permissions.helper'; import { IS_EE } from '../../../e2e/config/constants'; +import { createAgent, makeAgentAvailable } from '../../../data/livechat/rooms'; +import { getWorkHours } from '../../../data/livechat/businessHours'; describe('[CE] LIVECHAT - business hours', function () { this.retries(0); @@ -15,8 +17,11 @@ describe('[CE] LIVECHAT - business hours', function () { before(async () => { await updateSetting('Livechat_enabled', true); + await updateSetting('Livechat_enable_business_hours', true); + await createAgent(); }); + let defaultBhId: any; describe('livechat/business-hour', () => { it('should fail when user doesnt have view-livechat-business-hours permission', async () => { await updatePermission('view-livechat-business-hours', []); @@ -43,6 +48,36 @@ describe('[CE] LIVECHAT - business hours', function () { expect(response.body.businessHour.workHours[0].finish).to.be.an('object'); expect(response.body.businessHour.workHours[0].open).to.be.a('boolean'); expect(response.body.businessHour.timezone).to.be.an('object').that.has.property('name').that.is.an('string'); + + defaultBhId = response.body.businessHour; + }); + it('should not allow a user to be available if BH are closed', async () => { + await saveBusinessHour({ + ...defaultBhId, + workHours: [ + { + day: 'Monday', + open: true, + start: '00:00', + finish: '00:01', + }, + ], + }); + + const { body } = await makeAgentAvailable(credentials); + + expect(body).to.have.property('success', false); + expect(body.error).to.be.equal('error-business-hours-are-closed'); + }); + it('should allow a user to be available if BH are open', async () => { + await saveBusinessHour({ + ...defaultBhId, + workHours: getWorkHours(true), + }); + + const { body } = await makeAgentAvailable(credentials); + + expect(body).to.have.property('success', true); }); }); @@ -72,7 +107,7 @@ describe('[CE] LIVECHAT - business hours', function () { }); it('should return a just created custom business hour', async () => { const name = `business-hour-${Date.now()}`; - await updateSetting('Livechat_business_hour_type', 'multiple'); + await updateSetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.MULTIPLE); await saveBusinessHour({ name, active: true, @@ -109,5 +144,86 @@ describe('[CE] LIVECHAT - business hours', function () { expect(body.businessHours[0].workHours[0]).to.have.property('finish').that.is.an('object'); expect(body.businessHours[0]).to.have.property('timezone').that.is.an('object').with.property('name', 'America/Sao_Paulo'); }); + it('should fail if start and finish time are the same', async () => { + const name = `business-hour-${Date.now()}`; + await updateSetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.MULTIPLE); + const result = await saveBusinessHour({ + name, + active: true, + type: LivechatBusinessHourTypes.CUSTOM, + workHours: [ + { + day: 'Monday', + open: true, + // @ts-expect-error - this is valid for endpoint, actual type converts this into an object + start: '08:00', + // @ts-expect-error - same as previous one + finish: '08:00', + }, + ], + timezone: { + name: 'America/Sao_Paulo', + utc: '-03:00', + }, + departmentsToApplyBusinessHour: '', + timezoneName: 'America/Sao_Paulo', + }); + + expect(result).to.have.property('error'); + }); + it('should fail if finish is before start time', async () => { + const name = `business-hour-${Date.now()}`; + await updateSetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.MULTIPLE); + const result = await saveBusinessHour({ + name, + active: true, + type: LivechatBusinessHourTypes.CUSTOM, + workHours: [ + { + day: 'Monday', + open: true, + // @ts-expect-error - this is valid for endpoint, actual type converts this into an object + start: '10:00', + // @ts-expect-error - same as previous one + finish: '08:00', + }, + ], + timezone: { + name: 'America/Sao_Paulo', + utc: '-03:00', + }, + departmentsToApplyBusinessHour: '', + timezoneName: 'America/Sao_Paulo', + }); + + expect(result).to.have.property('error'); + }); + it('should fail if data is invalid', async () => { + const name = `business-hour-${Date.now()}`; + await updateSetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.MULTIPLE); + const result = await saveBusinessHour({ + name, + active: true, + type: LivechatBusinessHourTypes.CUSTOM, + workHours: [ + { + day: 'Monday', + open: true, + // @ts-expect-error - this is valid for endpoint, actual type converts this into an object + start: '20000', + // @ts-expect-error - same as previous one + finish: 'xxxxx', + }, + ], + timezone: { + name: 'America/Sao_Paulo', + utc: '-03:00', + }, + departmentsToApplyBusinessHour: '', + timezoneName: 'America/Sao_Paulo', + }); + + expect(result).to.have.property('error'); + }); }); }); From ef107614e53b4ba48ca3a5737f402cec824fdabe Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Tue, 27 Jun 2023 15:25:36 -0300 Subject: [PATCH 017/149] fix: Canned responses text editor without contrast in dark mode (#29636) --- .changeset/smooth-hotels-hunt.md | 5 +++++ .../CannedResponse/TextEditor/Textarea.tsx | 19 ++++--------------- 2 files changed, 9 insertions(+), 15 deletions(-) create mode 100644 .changeset/smooth-hotels-hunt.md diff --git a/.changeset/smooth-hotels-hunt.md b/.changeset/smooth-hotels-hunt.md new file mode 100644 index 000000000000..a8a0b5ef7dd7 --- /dev/null +++ b/.changeset/smooth-hotels-hunt.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed Canned Responses text editor having no contrast in dark mode. diff --git a/apps/meteor/ee/client/omnichannel/components/CannedResponse/TextEditor/Textarea.tsx b/apps/meteor/ee/client/omnichannel/components/CannedResponse/TextEditor/Textarea.tsx index 518628fa2029..69b7124bbb11 100644 --- a/apps/meteor/ee/client/omnichannel/components/CannedResponse/TextEditor/Textarea.tsx +++ b/apps/meteor/ee/client/omnichannel/components/CannedResponse/TextEditor/Textarea.tsx @@ -1,22 +1,11 @@ -import { Box } from '@rocket.chat/fuselage'; +import { TextAreaInput } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; import React, { forwardRef } from 'react'; -type TextareaProps = ComponentProps<typeof Box>; +type TextareaProps = ComponentProps<typeof TextAreaInput>; -const Textarea = forwardRef<Element, TextareaProps>(function Textarea(props, ref) { - return ( - <Box - is='textarea' - ref={ref} - w='full' - style={{ wordBreak: 'normal' }} - rcx-box--animated - rcx-input-box--type={'textarea'} - rcx-input-box--undecorated - {...props} - /> - ); +const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea(props, ref) { + return <TextAreaInput ref={ref} w='full' style={{ wordBreak: 'normal' }} rcx-input-box--undecorated {...props} />; }); export default Textarea; From 6f3eeec009ef3d233e7b022d9c13bfd2cd3b534b Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:40:00 -0300 Subject: [PATCH 018/149] fix(meteor): Video Record button disabled on iOS browsers (#29649) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .changeset/itchy-hotels-care.md | 5 +++++ .../ui/client/lib/recorderjs/videoRecorder.ts | 18 +++++++++++------- .../VideoMessageRecorder.tsx | 16 ++++++++++++---- .../actions/VideoMessageAction.tsx | 3 ++- 4 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 .changeset/itchy-hotels-care.md diff --git a/.changeset/itchy-hotels-care.md b/.changeset/itchy-hotels-care.md new file mode 100644 index 000000000000..93aed3ed6dce --- /dev/null +++ b/.changeset/itchy-hotels-care.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed video message button disabled on iOS browsers diff --git a/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts b/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts index 9d6a44521894..10424c5b3f86 100644 --- a/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts +++ b/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts @@ -17,6 +17,16 @@ class VideoRecorder { private mediaRecorder: MediaRecorder | undefined; + public getSupportedMimeTypes() { + if (window.MediaRecorder.isTypeSupported('video/webm')) { + return 'video/webm; codecs=vp8,opus'; + } + if (window.MediaRecorder.isTypeSupported('video/mp4')) { + return 'video/mp4'; + } + return ''; + } + public start(videoel?: HTMLVideoElement, cb?: (this: this, success: boolean) => void) { this.videoel = videoel; @@ -51,7 +61,7 @@ class VideoRecorder { return; } - this.mediaRecorder = new MediaRecorder(this.stream, { mimeType: 'video/webm; codecs=vp8,opus' }); + this.mediaRecorder = new MediaRecorder(this.stream, { mimeType: this.getSupportedMimeTypes() }); this.mediaRecorder.ondataavailable = (blobev) => { this.chunks.push(blobev.data); if (!this.recordingAvailable.get()) { @@ -66,7 +76,6 @@ class VideoRecorder { if (!this.videoel) { return; } - this.stream = stream; try { @@ -76,11 +85,6 @@ class VideoRecorder { this.videoel.src = URL.createObjectURL(stream as unknown as MediaSource | Blob); } - this.videoel.muted = true; - this.videoel.onloadedmetadata = () => { - void this.videoel?.play(); - }; - this.started = true; return this.cameraStarted.set(true); } diff --git a/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx b/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx index f9c1f2ed5a97..5d43a07d8b0b 100644 --- a/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx +++ b/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx @@ -29,6 +29,14 @@ const videoContainerClass = css` } `; +const getVideoRecordingExtension = () => { + const supported = VideoRecorder.getSupportedMimeTypes(); + if (supported.match(/video\/webm/)) { + return 'webm'; + } + return 'mp4'; +}; + const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessageRecorderProps) => { const t = useTranslation(); const videoRef = useRef<HTMLVideoElement>(null); @@ -75,8 +83,8 @@ const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessag const handleSendRecord = async () => { const cb = async (blob: Blob) => { - const fileName = `${t('Video_record')}.webm`; - const file = new File([blob], fileName, { type: 'video/webm' }); + const fileName = `${t('Video_record')}.${getVideoRecordingExtension()}`; + const file = new File([blob], fileName, { type: VideoRecorder.getSupportedMimeTypes().split(';')[0] }); await chat?.flows.uploadFiles([file]); chat?.composer?.setRecordingVideo(false); }; @@ -94,7 +102,7 @@ const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessag }; useEffect(() => { - if (!window.MediaRecorder.isTypeSupported('video/webm; codecs=vp8,opus')) { + if (!VideoRecorder.getSupportedMimeTypes()) { return dispatchToastMessage({ type: 'error', message: t('Browser_does_not_support_recording_video') }); } @@ -109,7 +117,7 @@ const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessag <PositionAnimated visible='visible' anchor={reference} placement='top-end'> <Box bg='light' padding={4} borderRadius={4} elevation='2'> <Box className={videoContainerClass} overflow='hidden' height={240} borderRadius={4}> - <video ref={videoRef} width={320} height={240} /> + <video muted autoPlay playsInline ref={videoRef} width={320} height={240} /> </Box> <Box mbs={4} display='flex' justifyContent='space-between'> <Button small onClick={handleRecord}> diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx index ec7796830a1d..d77be675243f 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx @@ -5,6 +5,7 @@ import { useTranslation, useSetting } from '@rocket.chat/ui-contexts'; import type { AllHTMLAttributes } from 'react'; import React, { useEffect, useMemo } from 'react'; +import { VideoRecorder } from '../../../../../../../../../app/ui/client/lib/recorderjs/videoRecorder'; import type { ChatAPI } from '../../../../../../../../lib/chats/ChatAPI'; import { useChat } from '../../../../../../contexts/ChatContext'; import { useMediaActionTitle } from '../../hooks/useMediaActionTitle'; @@ -33,7 +34,7 @@ const VideoMessageAction = ({ collapsed, chatContext, disabled, ...props }: Vide isVideoRecorderEnabled && !fileUploadMediaTypeBlackList?.match(/video\/webm|video\/\*/i) && (!fileUploadMediaTypeWhiteList || fileUploadMediaTypeWhiteList.match(/video\/webm|video\/\*/i)) && - window.MediaRecorder.isTypeSupported('video/webm; codecs=vp8,opus'), + Boolean(VideoRecorder.getSupportedMimeTypes()), ), [fileUploadMediaTypeBlackList, fileUploadMediaTypeWhiteList, isFileUploadEnabled, isPermissionDenied, isVideoRecorderEnabled], ); From 361864a745ed6578d3097985c99792db9d910791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Wed, 28 Jun 2023 13:33:16 -0300 Subject: [PATCH 019/149] chore: Menu v2 (#29580) --- .../AdministrationList/AdministrationList.tsx | 22 -- .../AdministrationModelList.spec.tsx | 141 ------------- .../AdministrationModelList.tsx | 107 ---------- .../AdministrationList/AppsModelList.spec.tsx | 141 ------------- .../AdministrationList/AppsModelList.tsx | 101 --------- .../AuditModelList.spec.tsx | 55 ----- .../AdministrationList/AuditModelList.tsx | 51 ----- apps/meteor/client/components/GenericMenu.tsx | 64 ++++++ .../client/components/GenericMenuItem.tsx | 21 ++ .../components/SortList/GroupingList.tsx | 54 ----- .../client/components/SortList/SortList.tsx | 21 -- .../components/SortList/SortModeList.tsx | 46 ----- .../components/SortList/ViewModeList.tsx | 65 ------ .../client/components/SortList/index.ts | 1 - .../client/hooks/useHandleMenuAction.tsx | 10 + .../client/providers/TooltipProvider.tsx | 2 +- .../sidebar/header/actions/Administration.tsx | 105 ++-------- .../sidebar/header/actions/CreateRoom.tsx | 42 ++-- .../sidebar/header/actions/CreateRoomList.tsx | 109 ---------- .../client/sidebar/header/actions/Sort.tsx | 27 +-- .../actions/hooks/useAdministrationItems.tsx | 94 +++++++++ .../actions/hooks/useAdministrationMenu.tsx | 75 +++++++ .../header/actions/hooks/useAppsItems.tsx | 78 +++++++ .../header/actions/hooks/useAuditItems.tsx | 30 +++ .../actions/hooks/useCreateRoomItems.tsx | 83 ++++++++ .../actions/hooks/useCreateRoomMenu.tsx | 24 +++ .../actions/hooks/useGroupingListItems.tsx | 43 ++++ .../hooks/useMatrixFederationItems.tsx.tsx | 26 +++ .../header/actions/hooks/useSortMenu.tsx | 17 ++ .../header/actions/hooks/useSortModeItems.tsx | 46 +++++ .../header/actions/hooks/useViewModeItems.tsx | 53 +++++ .../header/hooks/useCreateRoomModal.tsx | 8 +- .../tests/e2e/administration-menu.spec.ts | 2 +- .../page-objects/fragments/home-sidenav.ts | 6 +- .../header/actions/Administration.spec.tsx | 192 ------------------ ee/packages/ui-theming/src/paletteDark.ts | 2 +- yarn.lock | 34 ++-- 37 files changed, 731 insertions(+), 1267 deletions(-) delete mode 100644 apps/meteor/client/components/AdministrationList/AdministrationList.tsx delete mode 100644 apps/meteor/client/components/AdministrationList/AdministrationModelList.spec.tsx delete mode 100644 apps/meteor/client/components/AdministrationList/AdministrationModelList.tsx delete mode 100644 apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx delete mode 100644 apps/meteor/client/components/AdministrationList/AppsModelList.tsx delete mode 100644 apps/meteor/client/components/AdministrationList/AuditModelList.spec.tsx delete mode 100644 apps/meteor/client/components/AdministrationList/AuditModelList.tsx create mode 100644 apps/meteor/client/components/GenericMenu.tsx create mode 100644 apps/meteor/client/components/GenericMenuItem.tsx delete mode 100644 apps/meteor/client/components/SortList/GroupingList.tsx delete mode 100644 apps/meteor/client/components/SortList/SortList.tsx delete mode 100644 apps/meteor/client/components/SortList/SortModeList.tsx delete mode 100644 apps/meteor/client/components/SortList/ViewModeList.tsx delete mode 100644 apps/meteor/client/components/SortList/index.ts create mode 100644 apps/meteor/client/hooks/useHandleMenuAction.tsx delete mode 100644 apps/meteor/client/sidebar/header/actions/CreateRoomList.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx delete mode 100644 apps/meteor/tests/unit/client/sidebar/header/actions/Administration.spec.tsx diff --git a/apps/meteor/client/components/AdministrationList/AdministrationList.tsx b/apps/meteor/client/components/AdministrationList/AdministrationList.tsx deleted file mode 100644 index a57671a1534a..000000000000 --- a/apps/meteor/client/components/AdministrationList/AdministrationList.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { OptionDivider } from '@rocket.chat/fuselage'; -import type { ReactElement, ReactNode } from 'react'; -import React, { Fragment } from 'react'; - -type AdministrationListProps = { - optionsList: ReactNode[]; -}; - -const AdministrationList = ({ optionsList }: AdministrationListProps): ReactElement => { - return ( - <> - {optionsList.map((item, index) => ( - <Fragment key={index}> - {index > 0 && <OptionDivider />} - {item} - </Fragment> - ))} - </> - ); -}; - -export default AdministrationList; diff --git a/apps/meteor/client/components/AdministrationList/AdministrationModelList.spec.tsx b/apps/meteor/client/components/AdministrationList/AdministrationModelList.spec.tsx deleted file mode 100644 index 4d98d15f3e3c..000000000000 --- a/apps/meteor/client/components/AdministrationList/AdministrationModelList.spec.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { expect, spy } from 'chai'; -import proxyquire from 'proxyquire'; -import type { ReactNode } from 'react'; -import React from 'react'; - -import ModalContextMock from '../../../tests/mocks/client/ModalContextMock'; -import RouterContextMock from '../../../tests/mocks/client/RouterContextMock'; -import type * as AdministrationModelListModule from './AdministrationModelList'; - -describe('AdministrationModelList', () => { - const loadMock = (stubs?: Record<string, unknown>) => { - return proxyquire.noCallThru().load<typeof AdministrationModelListModule>('./AdministrationModelList', { - '../../../app/ui-utils/client': {}, - 'meteor/kadira:flow-router': { - FlowRouter: {}, - }, - '../../views/hooks/useUpgradeTabParams': { - useUpgradeTabParams: () => ({ - isLoading: false, - tabType: 'Upgrade', - trialEndDate: '2020-01-01', - }), - }, - '../../../lib/upgradeTab': { - getUpgradeTabLabel: () => 'Upgrade', - isFullyFeature: () => true, - }, - '../../../app/authorization/client': { - userHasAllPermission: () => true, - }, - '@tanstack/react-query': { - useQuery: () => '', - }, - ...stubs, - }).default; - }; - - it('should render administration', async () => { - const AdministrationModelList = loadMock(); - render(<AdministrationModelList accountBoxItems={[]} showWorkspace={true} onDismiss={() => null} />, { wrapper: ModalContextMock }); - - expect(screen.getByText('Administration')).to.exist; - expect(screen.getByText('Workspace')).to.exist; - expect(screen.getByText('Upgrade')).to.exist; - }); - - it('should not render workspace', async () => { - const AdministrationModelList = loadMock(); - render(<AdministrationModelList accountBoxItems={[]} showWorkspace={false} onDismiss={() => null} />, { wrapper: ModalContextMock }); - - expect(screen.getByText('Administration')).to.exist; - expect(screen.queryByText('Workspace')).to.not.exist; - expect(screen.getByText('Upgrade')).to.exist; - }); - - context('when clicked', () => { - const pushRoute = spy(); - const handleDismiss = spy(); - - const ProvidersMock = ({ children }: { children: ReactNode }) => { - return ( - <ModalContextMock> - <RouterContextMock pushRoute={pushRoute}>{children}</RouterContextMock> - </ModalContextMock> - ); - }; - - it('should go to admin index', async () => { - const AdministrationModelList = loadMock(); - - render(<AdministrationModelList accountBoxItems={[]} showWorkspace={true} onDismiss={handleDismiss} />, { wrapper: ProvidersMock }); - - const button = screen.getByText('Workspace'); - - userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('admin-index')); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - - it('should call upgrade route', async () => { - const AdministrationModelList = loadMock(); - - render(<AdministrationModelList accountBoxItems={[]} showWorkspace={false} onDismiss={handleDismiss} />, { wrapper: ProvidersMock }); - - const button = screen.getByText('Upgrade'); - - userEvent.click(button); - - await waitFor(() => expect(pushRoute).to.have.been.called.with('upgrade', { type: 'Upgrade' }, { trialEndDate: '2020-01-01' })); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - - it('should render admin box and call router', async () => { - const router = spy(); - const handleDismiss = spy(); - const AdministrationModelList = loadMock({ - 'meteor/kadira:flow-router': { - FlowRouter: { - go: router, - }, - }, - }); - - render( - <AdministrationModelList - accountBoxItems={[{ name: 'Admin Item', href: 'admin-item' } as any]} - showWorkspace={false} - onDismiss={handleDismiss} - />, - { wrapper: ProvidersMock }, - ); - - const button = screen.getByText('Admin Item'); - - userEvent.click(button); - - await waitFor(() => expect(router).to.have.been.called.with('admin-item')); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - - it('should render admin box and call sidenav', async () => { - const AdministrationModelList = loadMock(); - - render( - <AdministrationModelList - accountBoxItems={[{ name: 'Admin Item', sideNav: 'admin' } as any]} - showWorkspace={false} - onDismiss={handleDismiss} - />, - { wrapper: ProvidersMock }, - ); - - const button = screen.getByText('Admin Item'); - - userEvent.click(button); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - }); -}); diff --git a/apps/meteor/client/components/AdministrationList/AdministrationModelList.tsx b/apps/meteor/client/components/AdministrationList/AdministrationModelList.tsx deleted file mode 100644 index 3add4ad54128..000000000000 --- a/apps/meteor/client/components/AdministrationList/AdministrationModelList.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { OptionTitle } from '@rocket.chat/fuselage'; -import { useTranslation, useRoute, useMethod, useSetModal, useRole } from '@rocket.chat/ui-contexts'; -import { useQuery } from '@tanstack/react-query'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import type { FC } from 'react'; -import React from 'react'; - -import type { AccountBoxItem } from '../../../app/ui-utils/client/lib/AccountBox'; -import { getUpgradeTabLabel, isFullyFeature } from '../../../lib/upgradeTab'; -import RegisterWorkspaceModal from '../../views/admin/cloud/modals/RegisterWorkspaceModal'; -import { useUpgradeTabParams } from '../../views/hooks/useUpgradeTabParams'; -import Emoji from '../Emoji'; -import ListItem from '../Sidebar/ListItem'; - -type AdministrationModelListProps = { - accountBoxItems: AccountBoxItem[]; - showWorkspace: boolean; - onDismiss: () => void; -}; - -const AdministrationModelList: FC<AdministrationModelListProps> = ({ accountBoxItems, showWorkspace, onDismiss }) => { - const t = useTranslation(); - const { tabType, trialEndDate, isLoading } = useUpgradeTabParams(); - const shouldShowEmoji = isFullyFeature(tabType); - const label = getUpgradeTabLabel(tabType); - const isAdmin = useRole('admin'); - const setModal = useSetModal(); - - const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); - const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); - const { workspaceRegistered } = result.data || {}; - - const handleRegisterWorkspaceClick = (): void => { - const handleModalClose = (): void => setModal(null); - setModal(<RegisterWorkspaceModal onClose={handleModalClose} />); - }; - - const adminRoute = useRoute('admin-index'); - const upgradeRoute = useRoute('upgrade'); - const cloudRoute = useRoute('cloud'); - const showUpgradeItem = !isLoading && tabType; - - return ( - <> - <OptionTitle>{t('Administration')}</OptionTitle> - <ul> - {showUpgradeItem && ( - <ListItem - icon='arrow-stack-up' - role='listitem' - text={ - <> - {t(label)} {shouldShowEmoji && <Emoji emojiHandle=':zap:' />} - </> - } - onClick={() => { - upgradeRoute.push({ type: tabType }, trialEndDate ? { trialEndDate } : undefined); - onDismiss(); - }} - /> - )} - {isAdmin && ( - <ListItem - icon='cloud-plus' - role='listitem' - text={workspaceRegistered ? t('Registration') : t('Register')} - onClick={() => { - if (workspaceRegistered) { - cloudRoute.push({ context: '/' }); - onDismiss(); - return; - } - handleRegisterWorkspaceClick(); - }} - /> - )} - {showWorkspace && ( - <ListItem - icon='cog' - role='listitem' - text={t('Workspace')} - onClick={() => { - adminRoute.push({ context: '/' }); - onDismiss(); - }} - /> - )} - {accountBoxItems.length > 0 && ( - <> - {accountBoxItems.map((item, key) => { - const action = () => { - if (item.href) { - FlowRouter.go(item.href); - } - onDismiss(); - }; - - return <ListItem role='listitem' text={t(item.name)} icon={item.icon} onClick={action} key={item.name + key} />; - })} - </> - )} - </ul> - </> - ); -}; - -export default AdministrationModelList; diff --git a/apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx b/apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx deleted file mode 100644 index ffd2eabab93f..000000000000 --- a/apps/meteor/client/components/AdministrationList/AppsModelList.spec.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { expect, spy } from 'chai'; -import proxyquire from 'proxyquire'; -import type { ReactNode } from 'react'; -import React from 'react'; - -import RouterContextMock from '../../../tests/mocks/client/RouterContextMock'; -import type * as AppsModelListModel from './AppsModelList'; - -describe('AppsModelList', () => { - const loadMock = (stubs?: Record<string, unknown>) => { - return proxyquire.noCallThru().load<typeof AppsModelListModel>('./AppsModelList', { - '../../../app/ui-message/client/ActionManager': { - triggerActionButtonAction: {}, - }, - '../../views/marketplace/hooks/useAppRequestStats': { - useAppRequestStats: () => { - return { - isLoading: false, - isSuccess: true, - data: { - data: { - totalUnseen: 5, - }, - }, - }; - }, - }, - ...stubs, - }).default; - }; - - it('should render all apps options when a user has manage apps permission', async () => { - const AppsModelList = loadMock(); - - render(<AppsModelList onDismiss={() => null} appBoxItems={[]} appsManagementAllowed showMarketplace />); - - expect(screen.getByText('Apps')).to.exist; - expect(screen.getByText('Marketplace')).to.exist; - expect(screen.getByText('Installed')).to.exist; - expect(screen.getByText('Requested')).to.exist; - }); - - it('should render only marketplace and installed options when a user does not have manage apps permission', async () => { - const AppsModelList = loadMock({ - '@rocket.chat/ui-contexts': { - 'useAtLeastOnePermission': (): boolean => false, - '@noCallThru': false, - }, - }); - - render(<AppsModelList onDismiss={() => null} appBoxItems={[]} appsManagementAllowed={false} showMarketplace />); - - expect(screen.getByText('Apps')).to.exist; - expect(screen.getByText('Marketplace')).to.exist; - expect(screen.getByText('Installed')).to.exist; - expect(screen.queryByText('Requested')).to.not.exist; - }); - - it('should not render marketplace and installed options when user does not have access-marketplace permission', async () => { - const AppsModelList = loadMock({ - '@rocket.chat/ui-contexts': { - 'useAtLeastOnePermission': (): boolean => false, - '@noCallThru': false, - }, - }); - - render(<AppsModelList onDismiss={() => null} appBoxItems={[]} appsManagementAllowed={false} />); - - expect(screen.getByText('Apps')).to.exist; - expect(screen.queryByText('Marketplace')).to.not.exist; - expect(screen.queryByText('Installed')).to.not.exist; - expect(screen.queryByText('Requested')).to.not.exist; - }); - - context('when clicked', () => { - const pushRoute = spy(); - const handleDismiss = spy(); - - const ProvidersMock = ({ children }: { children: ReactNode }) => { - return <RouterContextMock pushRoute={pushRoute}>{children}</RouterContextMock>; - }; - - it('should go to marketplace', async () => { - const AppsModelList = loadMock(); - - render(<AppsModelList onDismiss={handleDismiss} appBoxItems={[]} showMarketplace />, { wrapper: ProvidersMock }); - - const button = screen.getByText('Marketplace'); - userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('marketplace', { context: 'explore', page: 'list' })); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - - it('should go to installed', async () => { - const AppsModelList = loadMock(); - - render(<AppsModelList onDismiss={handleDismiss} appBoxItems={[]} showMarketplace />, { wrapper: ProvidersMock }); - - const button = screen.getByText('Installed'); - - userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('marketplace', { context: 'installed', page: 'list' })); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - - it('should go to requested if user has manage apps permission', async () => { - const AppsModelList = loadMock(); - - render(<AppsModelList onDismiss={handleDismiss} appBoxItems={[]} appsManagementAllowed />, { wrapper: ProvidersMock }); - - const button = screen.getByText('Requested'); - - userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('marketplace', { context: 'requested', page: 'list' })); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - - it('should render apps and trigger action', async () => { - const triggerActionButtonAction = spy(); - - const AppsModelList = loadMock({ - '../../../app/ui-message/client/ActionManager': { - triggerActionButtonAction, - '@noCallThru': true, - }, - }); - - render(<AppsModelList onDismiss={handleDismiss} appBoxItems={[{ name: 'Custom App' } as any]} appsManagementAllowed />, { - wrapper: ProvidersMock, - }); - - const button = screen.getByText('Custom App'); - - userEvent.click(button); - await waitFor(() => expect(triggerActionButtonAction).to.have.been.called()); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - }); -}); diff --git a/apps/meteor/client/components/AdministrationList/AppsModelList.tsx b/apps/meteor/client/components/AdministrationList/AppsModelList.tsx deleted file mode 100644 index 35108f7ad0bc..000000000000 --- a/apps/meteor/client/components/AdministrationList/AppsModelList.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { Badge, OptionTitle, Skeleton } from '@rocket.chat/fuselage'; -import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import React from 'react'; - -import { triggerActionButtonAction } from '../../../app/ui-message/client/ActionManager'; -import type { IAppAccountBoxItem } from '../../../app/ui-utils/client/lib/AccountBox'; -import { useAppRequestStats } from '../../views/marketplace/hooks/useAppRequestStats'; -import ListItem from '../Sidebar/ListItem'; - -type AppsModelListProps = { - appBoxItems: IAppAccountBoxItem[]; - appsManagementAllowed?: boolean; - onDismiss: () => void; - showMarketplace?: boolean; -}; - -const AppsModelList = ({ appBoxItems, appsManagementAllowed, showMarketplace, onDismiss }: AppsModelListProps): ReactElement => { - const t = useTranslation(); - const marketplaceRoute = useRoute('marketplace'); - const page = 'list'; - - const appRequestStats = useAppRequestStats(); - - return ( - <> - <OptionTitle>{t('Apps')}</OptionTitle> - <ul> - <> - {showMarketplace && ( - <> - <ListItem - role='listitem' - icon='store' - text={t('Marketplace')} - onClick={() => { - marketplaceRoute.push({ context: 'explore', page }); - onDismiss(); - }} - /> - <ListItem - role='listitem' - icon='circle-arrow-down' - text={t('Installed')} - onClick={() => { - marketplaceRoute.push({ context: 'installed', page }); - onDismiss(); - }} - /> - </> - )} - - {appsManagementAllowed && ( - <> - <ListItem - role='listitem' - icon='cube' - text={t('Requested')} - onClick={(): void => { - marketplaceRoute.push({ context: 'requested', page }); - onDismiss(); - }} - > - {appRequestStats.isLoading && <Skeleton variant='circle' height={16} width={16} />} - {appRequestStats.isSuccess && appRequestStats.data.data.totalUnseen > 0 && ( - <Badge variant='primary'>{appRequestStats.data.data.totalUnseen}</Badge> - )} - </ListItem> - </> - )} - </> - {appBoxItems.length > 0 && ( - <> - {appBoxItems.map((item, key) => { - const action = () => { - triggerActionButtonAction({ - rid: '', - mid: '', - actionId: item.actionId, - appId: item.appId, - payload: { context: item.context }, - }); - onDismiss(); - }; - return ( - <ListItem - role='listitem' - text={(t.has(item.name) && t(item.name)) || item.name} - onClick={action} - key={item.actionId + key} - /> - ); - })} - </> - )} - </ul> - </> - ); -}; - -export default AppsModelList; diff --git a/apps/meteor/client/components/AdministrationList/AuditModelList.spec.tsx b/apps/meteor/client/components/AdministrationList/AuditModelList.spec.tsx deleted file mode 100644 index 086440343df8..000000000000 --- a/apps/meteor/client/components/AdministrationList/AuditModelList.spec.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { expect, spy } from 'chai'; -import type { ReactNode } from 'react'; -import React from 'react'; - -import RouterContextMock from '../../../tests/mocks/client/RouterContextMock'; -import AuditModelList from './AuditModelList'; - -describe('AuditModelList', () => { - it('should render audit', async () => { - render(<AuditModelList showAudit={true} showAuditLog={true} onDismiss={() => null} />); - - expect(screen.getByText('Audit')).to.exist; - expect(screen.getByText('Messages')).to.exist; - expect(screen.getByText('Logs')).to.exist; - }); - - it('should not render messages and log when does not have permission', async () => { - render(<AuditModelList showAudit={false} showAuditLog={false} onDismiss={() => null} />); - - expect(screen.getByText('Audit')).to.exist; - expect(screen.queryByText('Messages')).to.not.exist; - expect(screen.queryByText('Logs')).to.not.exist; - }); - - context('when clicked', () => { - const pushRoute = spy(); - const handleDismiss = spy(); - - const ProvidersMock = ({ children }: { children: ReactNode }) => ( - <RouterContextMock pushRoute={pushRoute}>{children}</RouterContextMock> - ); - - it('should go to audit home', async () => { - render(<AuditModelList showAudit={true} showAuditLog={false} onDismiss={handleDismiss} />, { wrapper: ProvidersMock }); - - const button = screen.getByText('Messages'); - - userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('audit-home')); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - - it('should go to audit log', async () => { - render(<AuditModelList showAudit={false} showAuditLog={true} onDismiss={handleDismiss} />, { wrapper: ProvidersMock }); - - const button = screen.getByText('Logs'); - - userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('audit-log')); - await waitFor(() => expect(handleDismiss).to.have.been.called()); - }); - }); -}); diff --git a/apps/meteor/client/components/AdministrationList/AuditModelList.tsx b/apps/meteor/client/components/AdministrationList/AuditModelList.tsx deleted file mode 100644 index 61e5c3c2cabb..000000000000 --- a/apps/meteor/client/components/AdministrationList/AuditModelList.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { OptionTitle } from '@rocket.chat/fuselage'; -import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; -import type { FC } from 'react'; -import React from 'react'; - -import ListItem from '../Sidebar/ListItem'; - -type AuditModelListProps = { - onDismiss: () => void; - showAudit: boolean; - showAuditLog: boolean; -}; - -const AuditModelList: FC<AuditModelListProps> = ({ showAudit, showAuditLog, onDismiss }) => { - const t = useTranslation(); - - const auditHomeRoute = useRoute('audit-home'); - const auditSettingsRoute = useRoute('audit-log'); - - return ( - <> - <OptionTitle>{t('Audit')}</OptionTitle> - <ul> - {showAudit && ( - <ListItem - role='listitem' - icon='document-eye' - text={t('Messages')} - onClick={() => { - auditHomeRoute.push(); - onDismiss(); - }} - /> - )} - {showAuditLog && ( - <ListItem - role='listitem' - icon='document-eye' - text={t('Logs')} - onClick={() => { - auditSettingsRoute.push(); - onDismiss(); - }} - /> - )} - </ul> - </> - ); -}; - -export default AuditModelList; diff --git a/apps/meteor/client/components/GenericMenu.tsx b/apps/meteor/client/components/GenericMenu.tsx new file mode 100644 index 000000000000..21a413a86644 --- /dev/null +++ b/apps/meteor/client/components/GenericMenu.tsx @@ -0,0 +1,64 @@ +import type { Icon } from '@rocket.chat/fuselage'; +import { MenuItem, MenuSection, MenuV2 } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { ComponentProps } from 'react'; +import React from 'react'; + +import type { GenericMenuItemProps } from './GenericMenuItem'; +import GenericMenuItem from './GenericMenuItem'; + +type GenericMenuCommonProps = { + icon?: ComponentProps<typeof Icon>['name']; + title: string; +}; +type GenericMenuConditionalProps = + | { + sections?: { + title?: string; + items: GenericMenuItemProps[]; + permission?: boolean | '' | 0 | null | undefined; + }[]; + items?: never; + } + | { + items?: GenericMenuItemProps[]; + sections?: never; + }; + +type GenericMenuProps = GenericMenuCommonProps & GenericMenuConditionalProps & Omit<ComponentProps<typeof MenuV2>, 'children'>; + +const GenericMenu = ({ title, icon = 'menu', ...props }: GenericMenuProps) => { + const t = useTranslation(); + + const sections = 'sections' in props && props.sections; + const items = 'items' in props && props.items; + + return ( + <> + {sections && ( + <MenuV2 icon={icon} title={t.has(title) ? t(title) : title} {...props}> + {sections.map(({ title, items }, key) => ( + <MenuSection title={title && (t.has(title) ? t(title) : title)} items={items} key={`${title}-${key}`}> + {(item) => ( + <MenuItem key={item.id}> + <GenericMenuItem {...item} /> + </MenuItem> + )} + </MenuSection> + ))} + </MenuV2> + )} + {items && ( + <MenuV2 icon={icon} title={t.has(title) ? t(title) : title} {...props}> + {items.map((item) => ( + <MenuItem key={item.id}> + <GenericMenuItem {...item} /> + </MenuItem> + ))} + </MenuV2> + )} + </> + ); +}; + +export default GenericMenu; diff --git a/apps/meteor/client/components/GenericMenuItem.tsx b/apps/meteor/client/components/GenericMenuItem.tsx new file mode 100644 index 000000000000..ad546e0473ef --- /dev/null +++ b/apps/meteor/client/components/GenericMenuItem.tsx @@ -0,0 +1,21 @@ +import { MenuItemContent, MenuItemIcon, MenuItemInput } from '@rocket.chat/fuselage'; +import type { ComponentProps, ReactNode } from 'react'; +import React from 'react'; + +export type GenericMenuItemProps = { + id?: string; + icon?: ComponentProps<typeof MenuItemIcon>['name']; + content?: ReactNode; + addon?: ReactNode; + onClick?: () => void; +}; + +const GenericMenuItem = ({ icon, content, addon }: GenericMenuItemProps) => ( + <> + {icon && <MenuItemIcon name={icon} />} + {content && <MenuItemContent>{content}</MenuItemContent>} + {addon && <MenuItemInput>{addon}</MenuItemInput>} + </> +); + +export default GenericMenuItem; diff --git a/apps/meteor/client/components/SortList/GroupingList.tsx b/apps/meteor/client/components/SortList/GroupingList.tsx deleted file mode 100644 index 4cb72b5f5bc8..000000000000 --- a/apps/meteor/client/components/SortList/GroupingList.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { CheckBox, OptionTitle } from '@rocket.chat/fuselage'; -import { useUserPreference, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import React, { useCallback } from 'react'; - -import ListItem from '../Sidebar/ListItem'; - -const GroupingList = function GroupingList(): ReactElement { - const t = useTranslation(); - - const sidebarGroupByType = useUserPreference<boolean>('sidebarGroupByType'); - const sidebarShowFavorites = useUserPreference<boolean>('sidebarShowFavorites'); - const sidebarShowUnread = useUserPreference<boolean>('sidebarShowUnread'); - - const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); - - const useHandleChange = (key: 'sidebarGroupByType' | 'sidebarShowFavorites' | 'sidebarShowUnread', value: boolean): (() => void) => - useCallback(() => saveUserPreferences({ data: { [key]: value } }), [key, value]); - - const handleChangeGroupByType = useHandleChange('sidebarGroupByType', !sidebarGroupByType); - const handleChangeShoFavorite = useHandleChange('sidebarShowFavorites', !sidebarShowFavorites); - const handleChangeShowUnread = useHandleChange('sidebarShowUnread', !sidebarShowUnread); - - return ( - <> - <OptionTitle>{t('Group_by')}</OptionTitle> - <ul> - <ListItem - is='label' - role='listitem' - icon='flag' - text={t('Unread')} - input={<CheckBox mi='x16' onChange={handleChangeShowUnread} checked={sidebarShowUnread} />} - /> - <ListItem - is='label' - role='listitem' - icon='star' - text={t('Favorites')} - input={<CheckBox mi='x16' onChange={handleChangeShoFavorite} checked={sidebarShowFavorites} />} - /> - <ListItem - is='label' - role='listitem' - icon='group-by-type' - text={t('Types')} - input={<CheckBox mi='x16' onChange={handleChangeGroupByType} checked={sidebarGroupByType} />} - /> - </ul> - </> - ); -}; - -export default GroupingList; diff --git a/apps/meteor/client/components/SortList/SortList.tsx b/apps/meteor/client/components/SortList/SortList.tsx deleted file mode 100644 index 64fcd9751952..000000000000 --- a/apps/meteor/client/components/SortList/SortList.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { OptionDivider } from '@rocket.chat/fuselage'; -import type { ReactElement } from 'react'; -import React from 'react'; - -import GroupingList from './GroupingList'; -import SortModeList from './SortModeList'; -import ViewModeList from './ViewModeList'; - -function SortList(): ReactElement { - return ( - <> - <ViewModeList /> - <OptionDivider /> - <SortModeList /> - <OptionDivider /> - <GroupingList /> - </> - ); -} - -export default SortList; diff --git a/apps/meteor/client/components/SortList/SortModeList.tsx b/apps/meteor/client/components/SortList/SortModeList.tsx deleted file mode 100644 index 172b26bc7afd..000000000000 --- a/apps/meteor/client/components/SortList/SortModeList.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { RadioButton, OptionTitle } from '@rocket.chat/fuselage'; -import { useUserPreference, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import React, { useCallback } from 'react'; - -import { useOmnichannelEnterpriseEnabled } from '../../hooks/omnichannel/useOmnichannelEnterpriseEnabled'; -import { OmnichannelSortingDisclaimer } from '../Omnichannel/OmnichannelSortingDisclaimer'; -import ListItem from '../Sidebar/ListItem'; - -function SortModeList(): ReactElement { - const t = useTranslation(); - const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); - const sidebarSortBy = useUserPreference<'activity' | 'alphabetical'>('sidebarSortby', 'activity'); - const isOmnichannelEnabled = useOmnichannelEnterpriseEnabled(); - - const useHandleChange = (value: 'alphabetical' | 'activity'): (() => void) => - useCallback(() => saveUserPreferences({ data: { sidebarSortby: value } }), [value]); - - const setToAlphabetical = useHandleChange('alphabetical'); - const setToActivity = useHandleChange('activity'); - - return ( - <> - <OptionTitle>{t('Sort_By')}</OptionTitle> - <ul> - <ListItem - is='label' - role='listitem' - icon='clock' - text={t('Activity')} - input={<RadioButton mi='x16' onChange={setToActivity} checked={sidebarSortBy === 'activity'} />} - /> - <ListItem - is='label' - role='listitem' - icon='sort-az' - text={t('Name')} - input={<RadioButton mi='x16' onChange={setToAlphabetical} checked={sidebarSortBy === 'alphabetical'} />} - /> - </ul> - {isOmnichannelEnabled && <OmnichannelSortingDisclaimer id='sortByList' />} - </> - ); -} - -export default SortModeList; diff --git a/apps/meteor/client/components/SortList/ViewModeList.tsx b/apps/meteor/client/components/SortList/ViewModeList.tsx deleted file mode 100644 index ea36f93097db..000000000000 --- a/apps/meteor/client/components/SortList/ViewModeList.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { ToggleSwitch, RadioButton, OptionTitle } from '@rocket.chat/fuselage'; -import { useUserPreference, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import React, { useCallback } from 'react'; - -import ListItem from '../Sidebar/ListItem'; - -function ViewModeList(): ReactElement { - const t = useTranslation(); - - const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); - - const useHandleChange = (value: 'medium' | 'extended' | 'condensed'): (() => void) => - useCallback(() => saveUserPreferences({ data: { sidebarViewMode: value } }), [value]); - - const sidebarViewMode = useUserPreference<'medium' | 'extended' | 'condensed'>('sidebarViewMode', 'extended'); - const sidebarDisplayAvatar = useUserPreference('sidebarDisplayAvatar', false); - - const setToExtended = useHandleChange('extended'); - const setToMedium = useHandleChange('medium'); - const setToCondensed = useHandleChange('condensed'); - - const handleChangeSidebarDisplayAvatar = useCallback( - () => saveUserPreferences({ data: { sidebarDisplayAvatar: !sidebarDisplayAvatar } }), - [saveUserPreferences, sidebarDisplayAvatar], - ); - - return ( - <> - <OptionTitle>{t('Display')}</OptionTitle> - <ul> - <ListItem - is='label' - role='listitem' - icon='extended-view' - text={t('Extended')} - input={<RadioButton mi='x16' onChange={setToExtended} checked={sidebarViewMode === 'extended'} />} - /> - <ListItem - is='label' - role='listitem' - icon='medium-view' - text={t('Medium')} - input={<RadioButton mi='x16' onChange={setToMedium} checked={sidebarViewMode === 'medium'} />} - /> - <ListItem - is='label' - role='listitem' - icon='condensed-view' - text={t('Condensed')} - input={<RadioButton mi='x16' onChange={setToCondensed} checked={sidebarViewMode === 'condensed'} />} - /> - <ListItem - is='label' - role='listitem' - icon='user-rounded' - text={t('Avatars')} - input={<ToggleSwitch mie='x16' onChange={handleChangeSidebarDisplayAvatar} checked={sidebarDisplayAvatar} />} - /> - </ul> - </> - ); -} - -export default ViewModeList; diff --git a/apps/meteor/client/components/SortList/index.ts b/apps/meteor/client/components/SortList/index.ts deleted file mode 100644 index eeb9d07165bb..000000000000 --- a/apps/meteor/client/components/SortList/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './SortList'; diff --git a/apps/meteor/client/hooks/useHandleMenuAction.tsx b/apps/meteor/client/hooks/useHandleMenuAction.tsx new file mode 100644 index 000000000000..f799ab464e7b --- /dev/null +++ b/apps/meteor/client/hooks/useHandleMenuAction.tsx @@ -0,0 +1,10 @@ +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import type { GenericMenuItemProps } from '../components/GenericMenuItem'; + +export const useHandleMenuAction = (items: GenericMenuItemProps[]) => { + return useMutableCallback((id) => { + const item = items.find((item) => item.id === id); + item?.onClick && item.onClick(); + }); +}; diff --git a/apps/meteor/client/providers/TooltipProvider.tsx b/apps/meteor/client/providers/TooltipProvider.tsx index 2b4d7026ed38..0fc7c996b9f3 100644 --- a/apps/meteor/client/providers/TooltipProvider.tsx +++ b/apps/meteor/client/providers/TooltipProvider.tsx @@ -121,7 +121,7 @@ const TooltipProvider: FC = ({ children }) => { document.body.addEventListener('mouseover', handleMouseOver, { passive: true, }); - document.body.addEventListener('click', dismissOnClick); + document.body.addEventListener('click', dismissOnClick, { capture: true }); return (): void => { contextValue.close(); diff --git a/apps/meteor/client/sidebar/header/actions/Administration.tsx b/apps/meteor/client/sidebar/header/actions/Administration.tsx index 6aac75710861..cb6921f91dda 100644 --- a/apps/meteor/client/sidebar/header/actions/Administration.tsx +++ b/apps/meteor/client/sidebar/header/actions/Administration.tsx @@ -1,103 +1,22 @@ -import { Sidebar, Dropdown } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { usePermission, useAtLeastOnePermission } from '@rocket.chat/ui-contexts'; +import { Sidebar } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes, VFC } from 'react'; -import React, { useCallback, useRef } from 'react'; -import { createPortal } from 'react-dom'; +import React from 'react'; -import { AccountBox } from '../../../../app/ui-utils/client'; -import type { IAppAccountBoxItem, AccountBoxItem } from '../../../../app/ui-utils/client/lib/AccountBox'; -import { isAppAccountBoxItem } from '../../../../app/ui-utils/client/lib/AccountBox'; -import { useHasLicenseModule } from '../../../../ee/client/hooks/useHasLicenseModule'; -import AdministrationList from '../../../components/AdministrationList/AdministrationList'; -import AdministrationModelList from '../../../components/AdministrationList/AdministrationModelList'; -import AppsModelList from '../../../components/AdministrationList/AppsModelList'; -import AuditModelList from '../../../components/AdministrationList/AuditModelList'; -import { useReactiveValue } from '../../../hooks/useReactiveValue'; -import { useDropdownVisibility } from '../hooks/useDropdownVisibility'; - -const ADMIN_PERMISSIONS = [ - 'view-statistics', - 'run-import', - 'view-user-administration', - 'view-room-administration', - 'create-invite-links', - 'manage-cloud', - 'view-logs', - 'manage-sounds', - 'view-federation-data', - 'manage-email-inbox', - 'manage-emoji', - 'manage-outgoing-integrations', - 'manage-own-outgoing-integrations', - 'manage-incoming-integrations', - 'manage-own-incoming-integrations', - 'manage-oauth-apps', - 'access-mailer', - 'manage-user-status', - 'access-permissions', - 'access-setting-permissions', - 'view-privileged-setting', - 'edit-privileged-setting', - 'manage-selected-settings', - 'view-engagement-dashboard', - 'view-moderation-console', -]; +import GenericMenu from '../../../components/GenericMenu'; +import type { GenericMenuItemProps } from '../../../components/GenericMenuItem'; +import { useHandleMenuAction } from '../../../hooks/useHandleMenuAction'; +import { useAdministrationMenu } from './hooks/useAdministrationMenu'; const Administration: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { - const reference = useRef(null); - const target = useRef(null); - - const { isVisible, toggle } = useDropdownVisibility({ reference, target }); - - const getAccountBoxItems = useMutableCallback(() => AccountBox.getItems()); - const accountBoxItems = useReactiveValue(getAccountBoxItems); - - const hasAuditLicense = useHasLicenseModule('auditing') === true; - const hasManageAppsPermission = usePermission('manage-apps'); - const hasAccessMarketplacePermission = usePermission('access-marketplace'); - const hasAdminPermission = useAtLeastOnePermission(ADMIN_PERMISSIONS); - const hasAuditPermission = usePermission('can-audit') && hasAuditLicense; - const hasAuditLogPermission = usePermission('can-audit-log') && hasAuditLicense; - - const appBoxItems = accountBoxItems.filter((item): item is IAppAccountBoxItem => isAppAccountBoxItem(item)); - const adminBoxItems = accountBoxItems.filter((item): item is AccountBoxItem => !isAppAccountBoxItem(item)); - const showAdmin = hasAdminPermission || !!adminBoxItems.length; - const showAudit = hasAuditPermission || hasAuditLogPermission; - const showWorkspace = hasAdminPermission; - const showApps = hasAccessMarketplacePermission || hasManageAppsPermission || !!appBoxItems.length; - - const onDismiss = useCallback((): void => toggle(false), [toggle]); + const t = useTranslation(); - const optionsList = [ - showAdmin && <AdministrationModelList showWorkspace={showWorkspace} accountBoxItems={adminBoxItems} onDismiss={onDismiss} />, - showApps && ( - <AppsModelList - appBoxItems={appBoxItems} - onDismiss={onDismiss} - appsManagementAllowed={hasManageAppsPermission} - showMarketplace={hasAccessMarketplacePermission || hasManageAppsPermission} - /> - ), - showAudit && <AuditModelList showAudit={hasAuditPermission} showAuditLog={hasAuditLogPermission} onDismiss={onDismiss} />, - ].filter(Boolean); + const sections = useAdministrationMenu(); + const items = sections.reduce((acc, { items }) => [...acc, ...items], [] as GenericMenuItemProps[]); - if (!optionsList || optionsList.length === 0) { - return null; - } + const handleAction = useHandleMenuAction(items); - return ( - <> - <Sidebar.TopBar.Action icon='menu' onClick={(): void => toggle()} {...props} ref={reference} /> - {isVisible && - createPortal( - <Dropdown reference={reference} ref={target}> - <AdministrationList optionsList={optionsList} /> - </Dropdown>, - document.body, - )} - </> - ); + return <GenericMenu sections={sections} title={t('Administration')} onAction={handleAction} is={Sidebar.TopBar.Action} {...props} />; }; export default Administration; diff --git a/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx b/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx index 2c9d4f5ed3ee..465bcc63bc05 100644 --- a/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx +++ b/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx @@ -1,32 +1,30 @@ -import { Sidebar, Dropdown } from '@rocket.chat/fuselage'; -import { useAtLeastOnePermission } from '@rocket.chat/ui-contexts'; +import { Sidebar } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes, VFC } from 'react'; -import React, { useRef } from 'react'; -import { createPortal } from 'react-dom'; +import React from 'react'; -import { useDropdownVisibility } from '../hooks/useDropdownVisibility'; -import CreateRoomList from './CreateRoomList'; - -const CREATE_ROOM_PERMISSIONS = ['create-c', 'create-p', 'create-d', 'start-discussion', 'start-discussion-other-user']; +import GenericMenu from '../../../components/GenericMenu'; +import type { GenericMenuItemProps } from '../../../components/GenericMenuItem'; +import { useHandleMenuAction } from '../../../hooks/useHandleMenuAction'; +import { useCreateRoom } from './hooks/useCreateRoomMenu'; const CreateRoom: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { - const reference = useRef(null); - const target = useRef(null); - const { isVisible, toggle } = useDropdownVisibility({ reference, target }); + const t = useTranslation(); + + const sections = useCreateRoom(); + const items = sections.reduce((acc, { items }) => [...acc, ...items], [] as GenericMenuItemProps[]); - const showCreate = useAtLeastOnePermission(CREATE_ROOM_PERMISSIONS); + const handleAction = useHandleMenuAction(items); return ( - <> - {showCreate && <Sidebar.TopBar.Action icon='edit-rounded' onClick={(): void => toggle()} {...props} ref={reference} />} - {isVisible && - createPortal( - <Dropdown reference={reference} ref={target}> - <CreateRoomList closeList={(): void => toggle(false)} /> - </Dropdown>, - document.body, - )} - </> + <GenericMenu + icon='edit-rounded' + sections={sections} + onAction={handleAction} + title={t('Create_new')} + is={Sidebar.TopBar.Action} + {...props} + /> ); }; diff --git a/apps/meteor/client/sidebar/header/actions/CreateRoomList.tsx b/apps/meteor/client/sidebar/header/actions/CreateRoomList.tsx deleted file mode 100644 index f7ae91923028..000000000000 --- a/apps/meteor/client/sidebar/header/actions/CreateRoomList.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { OptionTitle, OptionDivider } from '@rocket.chat/fuselage'; -import { useSetting, useAtLeastOnePermission, useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, MouseEvent } from 'react'; -import React from 'react'; - -import CreateDiscussion from '../../../components/CreateDiscussion'; -import ListItem from '../../../components/Sidebar/ListItem'; -import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; -import CreateChannelWithData from '../CreateChannel'; -import CreateDirectMessage from '../CreateDirectMessage'; -import CreateTeam from '../CreateTeam'; -import MatrixFederationSearch from '../MatrixFederationSearch'; -import { useCreateRoomModal } from '../hooks/useCreateRoomModal'; - -const CREATE_CHANNEL_PERMISSIONS = ['create-c', 'create-p']; -const CREATE_TEAM_PERMISSIONS = ['create-team']; -const CREATE_DIRECT_PERMISSIONS = ['create-d']; -const CREATE_DISCUSSION_PERMISSIONS = ['start-discussion', 'start-discussion-other-user']; - -type CreateRoomListProps = { - closeList: () => void; -}; - -const CreateRoomList = ({ closeList }: CreateRoomListProps): ReactElement => { - const t = useTranslation(); - const discussionEnabled = useSetting('Discussion_enabled'); - - const canCreateChannel = useAtLeastOnePermission(CREATE_CHANNEL_PERMISSIONS); - const canCreateTeam = useAtLeastOnePermission(CREATE_TEAM_PERMISSIONS); - const canCreateDirectMessages = useAtLeastOnePermission(CREATE_DIRECT_PERMISSIONS); - const canCreateDiscussion = useAtLeastOnePermission(CREATE_DISCUSSION_PERMISSIONS); - - const createChannel = useCreateRoomModal(CreateChannelWithData); - const createTeam = useCreateRoomModal(CreateTeam); - const createDiscussion = useCreateRoomModal(CreateDiscussion); - const createDirectMessage = useCreateRoomModal(CreateDirectMessage); - const searchFederatedRooms = useCreateRoomModal(MatrixFederationSearch); - - const { data } = useIsEnterprise(); - const isMatrixEnabled = useSetting('Federation_Matrix_enabled') && data?.isEnterprise; - - return ( - <> - <OptionTitle>{t('Create_new')}</OptionTitle> - <ul> - {canCreateChannel && ( - <ListItem - role='listitem' - icon='hashtag' - text={t('Channel')} - onClick={(e: MouseEvent<HTMLElement>): void => { - createChannel(e); - closeList(); - }} - /> - )} - {canCreateTeam && ( - <ListItem - role='listitem' - icon='team' - text={t('Team')} - onClick={(e: MouseEvent<HTMLElement>): void => { - createTeam(e); - closeList(); - }} - /> - )} - {canCreateDirectMessages && ( - <ListItem - role='listitem' - icon='balloon' - text={t('Direct_Messages')} - onClick={(e: MouseEvent<HTMLElement>): void => { - createDirectMessage(e); - closeList(); - }} - /> - )} - {discussionEnabled && canCreateDiscussion && ( - <ListItem - role='listitem' - icon='discussion' - text={t('Discussion')} - onClick={(e: MouseEvent<HTMLElement>): void => { - createDiscussion(e); - closeList(); - }} - /> - )} - {isMatrixEnabled && ( - <> - <OptionDivider /> - <OptionTitle>{t('Explore')}</OptionTitle> - <ListItem - icon='magnifier' - text={t('Federation_Search_federated_rooms')} - onClick={(e: MouseEvent<HTMLElement>): void => { - searchFederatedRooms(e); - closeList(); - }} - /> - </> - )} - </ul> - </> - ); -}; - -export default CreateRoomList; diff --git a/apps/meteor/client/sidebar/header/actions/Sort.tsx b/apps/meteor/client/sidebar/header/actions/Sort.tsx index ac993807b236..194e920ec4a0 100644 --- a/apps/meteor/client/sidebar/header/actions/Sort.tsx +++ b/apps/meteor/client/sidebar/header/actions/Sort.tsx @@ -1,27 +1,18 @@ -import { Sidebar, Dropdown } from '@rocket.chat/fuselage'; +import { Sidebar } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; import type { VFC, HTMLAttributes } from 'react'; -import React, { useRef } from 'react'; -import { createPortal } from 'react-dom'; +import React from 'react'; -import SortList from '../../../components/SortList'; -import { useDropdownVisibility } from '../hooks/useDropdownVisibility'; +import GenericMenu from '../../../components/GenericMenu'; +import { useSortMenu } from './hooks/useSortMenu'; const Sort: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { - const reference = useRef(null); - const target = useRef(null); - const { isVisible, toggle } = useDropdownVisibility({ reference, target }); + const t = useTranslation(); + + const sections = useSortMenu(); return ( - <> - <Sidebar.TopBar.Action {...props} icon='sort' onClick={(): void => toggle()} ref={reference} /> - {isVisible && - createPortal( - <Dropdown reference={reference} ref={target}> - <SortList /> - </Dropdown>, - document.body, - )} - </> + <GenericMenu icon='sort' sections={sections} title={t('Display')} selectionMode='multiple' is={Sidebar.TopBar.Action} {...props} /> ); }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx new file mode 100644 index 000000000000..b32ae66dde13 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -0,0 +1,94 @@ +import { useTranslation, useRoute, useMethod, useSetModal, useRole } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; +import { FlowRouter } from 'meteor/kadira:flow-router'; +import React from 'react'; + +import type { AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; +import type { UpgradeTabVariant } from '../../../../../lib/upgradeTab'; +import { getUpgradeTabLabel, isFullyFeature } from '../../../../../lib/upgradeTab'; +import Emoji from '../../../../components/Emoji'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import RegisterWorkspaceModal from '../../../../views/admin/cloud/modals/RegisterWorkspaceModal'; +import { useUpgradeTabParams } from '../../../../views/hooks/useUpgradeTabParams'; + +type useAdministrationItemProps = { + accountBoxItems: AccountBoxItem[]; + showWorkspace: boolean; +}; +export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAdministrationItemProps): GenericMenuItemProps[] => { + const t = useTranslation(); + + const { tabType, trialEndDate, isLoading } = useUpgradeTabParams(); + const shouldShowEmoji = isFullyFeature(tabType); + const label = getUpgradeTabLabel(tabType); + const isAdmin = useRole('admin'); + const setModal = useSetModal(); + + const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); + const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); + const { workspaceRegistered } = result.data || {}; + + const handleRegisterWorkspaceClick = (): void => { + const handleModalClose = (): void => setModal(null); + setModal(<RegisterWorkspaceModal onClose={handleModalClose} />); + }; + + const adminRoute = useRoute('admin-index'); + const upgradeRoute = useRoute('upgrade'); + const cloudRoute = useRoute('cloud'); + const showUpgradeItem = !isLoading && tabType; + + const upgradeItem: GenericMenuItemProps = { + id: 'showUpgradeItem', + content: ( + <> + {t(label)} {shouldShowEmoji && <Emoji emojiHandle=':zap:' />} + </> + ), + icon: 'arrow-stack-up', + onClick: () => { + upgradeRoute.push({ type: tabType as UpgradeTabVariant }, trialEndDate ? { trialEndDate } : undefined); + }, + }; + const adminItem: GenericMenuItemProps = { + id: 'registration', + content: workspaceRegistered ? t('Registration') : t('Register'), + icon: 'cloud-plus', + onClick: () => { + if (workspaceRegistered) { + cloudRoute.push({ context: '/' }); + return; + } + handleRegisterWorkspaceClick(); + }, + }; + const workspaceItem: GenericMenuItemProps = { + id: 'workspace', + content: t('Workspace'), + icon: 'cog', + onClick: () => { + adminRoute.push({ context: '/' }); + }, + }; + + const accountBoxItem: GenericMenuItemProps[] = accountBoxItems.map((item, key) => { + const action = () => { + if (item.href) { + FlowRouter.go(item.href); + } + }; + return { + id: `account-box-item-${key}`, + content: t(item.name), + icon: item.icon, + onClick: action, + }; + }); + + return [ + ...(showUpgradeItem ? [upgradeItem] : []), + ...(isAdmin ? [adminItem] : []), + ...(showWorkspace ? [workspaceItem] : []), + ...(accountBoxItems.length ? accountBoxItem : []), + ]; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx new file mode 100644 index 000000000000..5145ac30c0f4 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx @@ -0,0 +1,75 @@ +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useAtLeastOnePermission, usePermission } from '@rocket.chat/ui-contexts'; + +import { AccountBox } from '../../../../../app/ui-utils/client'; +import type { IAppAccountBoxItem, AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; +import { isAppAccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; +import { useHasLicenseModule } from '../../../../../ee/client/hooks/useHasLicenseModule'; +import { useReactiveValue } from '../../../../hooks/useReactiveValue'; +import { useAdministrationItems } from './useAdministrationItems'; +import { useAppsItems } from './useAppsItems'; +import { useAuditItems } from './useAuditItems'; + +const ADMIN_PERMISSIONS = [ + 'view-statistics', + 'run-import', + 'view-user-administration', + 'view-room-administration', + 'create-invite-links', + 'manage-cloud', + 'view-logs', + 'manage-sounds', + 'view-federation-data', + 'manage-email-inbox', + 'manage-emoji', + 'manage-outgoing-integrations', + 'manage-own-outgoing-integrations', + 'manage-incoming-integrations', + 'manage-own-incoming-integrations', + 'manage-oauth-apps', + 'access-mailer', + 'manage-user-status', + 'access-permissions', + 'access-setting-permissions', + 'view-privileged-setting', + 'edit-privileged-setting', + 'manage-selected-settings', + 'view-engagement-dashboard', + 'view-moderation-console', +]; + +export const useAdministrationMenu = () => { + const getAccountBoxItems = useMutableCallback(() => AccountBox.getItems()); + const accountBoxItems = useReactiveValue(getAccountBoxItems); + + const hasAuditLicense = useHasLicenseModule('auditing') === true; + const hasManageAppsPermission = usePermission('manage-apps'); + const hasAccessMarketplacePermission = usePermission('access-marketplace'); + const hasAdminPermission = useAtLeastOnePermission(ADMIN_PERMISSIONS); + const hasAuditPermission = usePermission('can-audit') && hasAuditLicense; + const hasAuditLogPermission = usePermission('can-audit-log') && hasAuditLicense; + + const appBoxItems = accountBoxItems.filter((item): item is IAppAccountBoxItem => isAppAccountBoxItem(item)); + const adminBoxItems = accountBoxItems.filter((item): item is AccountBoxItem => !isAppAccountBoxItem(item)); + + const showAdmin = hasAdminPermission || !!adminBoxItems.length; + const showAudit = hasAuditPermission || hasAuditLogPermission; + const showWorkspace = hasAdminPermission; + const showApps = hasAccessMarketplacePermission || hasManageAppsPermission || !!appBoxItems.length; + + const administrationItems = useAdministrationItems({ accountBoxItems: adminBoxItems, showWorkspace }); + const appItems = useAppsItems({ + appBoxItems, + appsManagementAllowed: hasManageAppsPermission, + showMarketplace: hasAccessMarketplacePermission || hasManageAppsPermission, + }); + const auditItems = useAuditItems({ showAudit: hasAuditPermission, showAuditLog: hasAuditLogPermission }); + + const sections = [ + { title: 'Administration', items: administrationItems, permission: showAdmin }, + { title: 'Apps', items: appItems, permission: showApps }, + { title: 'Audit', items: auditItems, permission: showAudit }, + ]; + + return sections.filter(({ permission }) => permission); +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx new file mode 100644 index 000000000000..66a5befe0f2b --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx @@ -0,0 +1,78 @@ +import { Badge, Skeleton } from '@rocket.chat/fuselage'; +import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { triggerActionButtonAction } from '../../../../../app/ui-message/client/ActionManager'; +import type { IAppAccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import { useAppRequestStats } from '../../../../views/marketplace/hooks/useAppRequestStats'; + +type useAppsItemsProps = { + appBoxItems: IAppAccountBoxItem[]; + appsManagementAllowed?: boolean; + showMarketplace?: boolean; +}; + +export const useAppsItems = ({ appBoxItems, appsManagementAllowed, showMarketplace }: useAppsItemsProps): GenericMenuItemProps[] => { + const t = useTranslation(); + + const marketplaceRoute = useRoute('marketplace'); + const page = 'list'; + + const appRequestStats = useAppRequestStats(); + + const marketPlaceItems: GenericMenuItemProps[] = [ + { + id: 'marketplace', + icon: 'store', + content: t('Marketplace'), + onClick: () => marketplaceRoute.push({ context: 'explore', page }), + }, + { + id: 'installed', + icon: 'circle-arrow-down', + content: t('Installed'), + onClick: () => marketplaceRoute.push({ context: 'installed', page }), + }, + ]; + + const appsManagementItem: GenericMenuItemProps = { + id: 'requested-apps', + icon: 'cube', + content: t('Requested'), + onClick: () => { + marketplaceRoute.push({ context: 'requested', page }); + }, + addon: ( + <> + {appRequestStats.isLoading && <Skeleton variant='circle' height={16} width={16} />} + {appRequestStats.isSuccess && appRequestStats.data.data.totalUnseen > 0 && ( + <Badge variant='primary'>{appRequestStats.data.data.totalUnseen}</Badge> + )} + </> + ), + }; + + const appItems: GenericMenuItemProps[] = appBoxItems.map((item: IAppAccountBoxItem, key: number) => { + const action = () => { + triggerActionButtonAction({ + rid: '', + mid: '', + actionId: item.actionId, + appId: item.appId, + payload: { context: item.context }, + }); + }; + return { + id: item.actionId + key, + icon: item.icon as GenericMenuItemProps['icon'], + content: (t.has(item.name) && t(item.name)) || item.name, + onClick: action, + }; + }); + return [ + ...(showMarketplace ? marketPlaceItems : []), + ...(appsManagementAllowed ? [appsManagementItem] : []), + ...(appBoxItems.length ? appItems : []), + ]; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx new file mode 100644 index 000000000000..3e9d98d74b18 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx @@ -0,0 +1,30 @@ +import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; + +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; + +type useAuditItemsProps = { + showAudit: boolean; + showAuditLog: boolean; +}; + +export const useAuditItems = ({ showAudit, showAuditLog }: useAuditItemsProps): GenericMenuItemProps[] => { + const t = useTranslation(); + + const auditHomeRoute = useRoute('audit-home'); + const auditSettingsRoute = useRoute('audit-log'); + + const auditMessageItem: GenericMenuItemProps = { + id: 'messages', + icon: 'document-eye', + content: t('Messages'), + onClick: () => auditHomeRoute.push(), + }; + const auditLogItem: GenericMenuItemProps = { + id: 'auditLog', + icon: 'document-eye', + content: t('Logs'), + onClick: () => auditSettingsRoute.push(), + }; + + return [...(showAudit ? [auditMessageItem] : []), ...(showAuditLog ? [auditLogItem] : [])]; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx new file mode 100644 index 000000000000..3cdca7adebc4 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx @@ -0,0 +1,83 @@ +import { useTranslation, useSetting, useAtLeastOnePermission } from '@rocket.chat/ui-contexts'; + +import CreateDiscussion from '../../../../components/CreateDiscussion'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import { useIsEnterprise } from '../../../../hooks/useIsEnterprise'; +import CreateChannelWithData from '../../CreateChannel'; +import CreateDirectMessage from '../../CreateDirectMessage'; +import CreateTeam from '../../CreateTeam'; +import MatrixFederationSearch from '../../MatrixFederationSearch'; +import { useCreateRoomModal } from '../../hooks/useCreateRoomModal'; + +const CREATE_CHANNEL_PERMISSIONS = ['create-c', 'create-p']; +const CREATE_TEAM_PERMISSIONS = ['create-team']; +const CREATE_DIRECT_PERMISSIONS = ['create-d']; +const CREATE_DISCUSSION_PERMISSIONS = ['start-discussion', 'start-discussion-other-user']; + +export const useCreateRoomItems = (): GenericMenuItemProps[] => { + const t = useTranslation(); + const discussionEnabled = useSetting('Discussion_enabled'); + + const canCreateChannel = useAtLeastOnePermission(CREATE_CHANNEL_PERMISSIONS); + const canCreateTeam = useAtLeastOnePermission(CREATE_TEAM_PERMISSIONS); + const canCreateDirectMessages = useAtLeastOnePermission(CREATE_DIRECT_PERMISSIONS); + const canCreateDiscussion = useAtLeastOnePermission(CREATE_DISCUSSION_PERMISSIONS); + + const createChannel = useCreateRoomModal(CreateChannelWithData); + const createTeam = useCreateRoomModal(CreateTeam); + const createDiscussion = useCreateRoomModal(CreateDiscussion); + const createDirectMessage = useCreateRoomModal(CreateDirectMessage); + const searchFederatedRooms = useCreateRoomModal(MatrixFederationSearch); + + const { data } = useIsEnterprise(); + const isMatrixEnabled = useSetting('Federation_Matrix_enabled') && data?.isEnterprise; + + const createChannelItem: GenericMenuItemProps = { + id: 'channel', + content: t('Channel'), + icon: 'hashtag', + onClick: () => { + createChannel(); + }, + }; + const createTeamItem: GenericMenuItemProps = { + id: 'team', + content: t('Team'), + icon: 'team', + onClick: () => { + createTeam(); + }, + }; + const createDirectMessageItem: GenericMenuItemProps = { + id: 'direct', + content: t('Direct_Messages'), + icon: 'balloon', + onClick: () => { + createDirectMessage(); + }, + }; + const createDiscussionItem: GenericMenuItemProps = { + id: 'discussion', + content: t('Discussion'), + icon: 'discussion', + onClick: () => { + createDiscussion(); + }, + }; + const matrixFederationSearchItem: GenericMenuItemProps = { + id: 'matrix-federation-search', + content: t('Federation_Search_federated_rooms'), + icon: 'magnifier', + onClick: () => { + searchFederatedRooms(); + }, + }; + + return [ + ...(canCreateChannel ? [createChannelItem] : []), + ...(canCreateTeam ? [createTeamItem] : []), + ...(canCreateDirectMessages ? [createDirectMessageItem] : []), + ...(canCreateDiscussion && discussionEnabled ? [createDiscussionItem] : []), + ...(isMatrixEnabled ? [matrixFederationSearchItem] : []), + ]; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx new file mode 100644 index 000000000000..7e4cbe8d138e --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx @@ -0,0 +1,24 @@ +import { useAtLeastOnePermission, useSetting } from '@rocket.chat/ui-contexts'; + +import { useIsEnterprise } from '../../../../hooks/useIsEnterprise'; +import { useCreateRoomItems } from './useCreateRoomItems'; +import { useMatrixFederationItems } from './useMatrixFederationItems.tsx'; + +const CREATE_ROOM_PERMISSIONS = ['create-c', 'create-p', 'create-d', 'start-discussion', 'start-discussion-other-user']; + +export const useCreateRoom = () => { + const showCreate = useAtLeastOnePermission(CREATE_ROOM_PERMISSIONS); + + const { data } = useIsEnterprise(); + const isMatrixEnabled = useSetting('Federation_Matrix_enabled') && data?.isEnterprise; + + const createRoomItems = useCreateRoomItems(); + const matrixFederationSearchItems = useMatrixFederationItems({ isMatrixEnabled }); + + const sections = [ + { title: 'Create_new', items: createRoomItems, permission: showCreate }, + { title: 'Explore', items: matrixFederationSearchItems, permission: showCreate && isMatrixEnabled }, + ]; + + return sections.filter((section) => section.permission); +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx new file mode 100644 index 000000000000..24629be905e3 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx @@ -0,0 +1,43 @@ +import { CheckBox } from '@rocket.chat/fuselage'; +import { useEndpoint, useUserPreference, useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useCallback } from 'react'; + +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; + +export const useGroupingListItems = (): GenericMenuItemProps[] => { + const t = useTranslation(); + + const sidebarGroupByType = useUserPreference<boolean>('sidebarGroupByType'); + const sidebarShowFavorites = useUserPreference<boolean>('sidebarShowFavorites'); + const sidebarShowUnread = useUserPreference<boolean>('sidebarShowUnread'); + + const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); + + const useHandleChange = (key: 'sidebarGroupByType' | 'sidebarShowFavorites' | 'sidebarShowUnread', value: boolean): (() => void) => + useCallback(() => saveUserPreferences({ data: { [key]: value } }), [key, value]); + + const handleChangeGroupByType = useHandleChange('sidebarGroupByType', !sidebarGroupByType); + const handleChangeShoFavorite = useHandleChange('sidebarShowFavorites', !sidebarShowFavorites); + const handleChangeShowUnread = useHandleChange('sidebarShowUnread', !sidebarShowUnread); + + return [ + { + id: 'unread', + content: t('Unread'), + icon: 'flag', + addon: <CheckBox mi='x16' onChange={handleChangeShowUnread} checked={sidebarShowUnread} />, + }, + { + id: 'favorites', + content: t('Favorites'), + icon: 'star', + addon: <CheckBox mi='x16' onChange={handleChangeShoFavorite} checked={sidebarShowFavorites} />, + }, + { + id: 'types', + content: t('Types'), + icon: 'group-by-type', + addon: <CheckBox mi='x16' onChange={handleChangeGroupByType} checked={sidebarGroupByType} />, + }, + ]; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx new file mode 100644 index 000000000000..76cd6d9458af --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx @@ -0,0 +1,26 @@ +import { useTranslation } from '@rocket.chat/ui-contexts'; + +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import MatrixFederationSearch from '../../MatrixFederationSearch'; +import { useCreateRoomModal } from '../../hooks/useCreateRoomModal'; + +export const useMatrixFederationItems = ({ + isMatrixEnabled, +}: { + isMatrixEnabled: string | number | boolean | null | undefined; +}): GenericMenuItemProps[] => { + const t = useTranslation(); + + const searchFederatedRooms = useCreateRoomModal(MatrixFederationSearch); + + const matrixFederationSearchItem: GenericMenuItemProps = { + id: 'matrix-federation-search', + content: t('Federation_Search_federated_rooms'), + icon: 'magnifier', + onClick: () => { + searchFederatedRooms(); + }, + }; + + return [...(isMatrixEnabled ? [matrixFederationSearchItem] : [])]; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx new file mode 100644 index 000000000000..8a3f6a56e590 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx @@ -0,0 +1,17 @@ +import { useGroupingListItems } from './useGroupingListItems'; +import { useSortModeItems } from './useSortModeItems'; +import { useViewModeItems } from './useViewModeItems'; + +export const useSortMenu = () => { + const viewModeItems = useViewModeItems(); + const sortModeItems = useSortModeItems(); + const groupingListItems = useGroupingListItems(); + + const sections = [ + { title: 'Display', items: viewModeItems }, + { title: 'Sort_By', items: sortModeItems }, + { title: 'Group_by', items: groupingListItems }, + ]; + + return sections; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx new file mode 100644 index 000000000000..c200fefd5689 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx @@ -0,0 +1,46 @@ +import { RadioButton } from '@rocket.chat/fuselage'; +import { useEndpoint, useUserPreference, useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useCallback } from 'react'; + +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import { OmnichannelSortingDisclaimer } from '../../../../components/Omnichannel/OmnichannelSortingDisclaimer'; +import { useOmnichannelEnterpriseEnabled } from '../../../../hooks/omnichannel/useOmnichannelEnterpriseEnabled'; + +export const useSortModeItems = (): GenericMenuItemProps[] => { + const t = useTranslation(); + + const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); + const sidebarSortBy = useUserPreference<'activity' | 'alphabetical'>('sidebarSortby', 'activity'); + const isOmnichannelEnabled = useOmnichannelEnterpriseEnabled(); + + const omniDisclaimerItem = { + id: 'sortByList', + content: <OmnichannelSortingDisclaimer id='sortByList' />, + }; + + const useHandleChange = (value: 'alphabetical' | 'activity'): (() => void) => + useCallback(() => saveUserPreferences({ data: { sidebarSortby: value } }), [value]); + + const setToAlphabetical = useHandleChange('alphabetical'); + const setToActivity = useHandleChange('activity'); + const items: GenericMenuItemProps[] = [ + { + id: 'activity', + content: t('Activity'), + icon: 'clock', + addon: <RadioButton mi='x16' onChange={setToActivity} checked={sidebarSortBy === 'activity'} />, + }, + { + id: 'name', + content: t('Name'), + icon: 'sort-az', + addon: <RadioButton mi='x16' onChange={setToAlphabetical} checked={sidebarSortBy === 'alphabetical'} />, + }, + ]; + + if (isOmnichannelEnabled) { + items.push(omniDisclaimerItem); + } + + return items; +}; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx new file mode 100644 index 000000000000..92da7101baf5 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx @@ -0,0 +1,53 @@ +import { RadioButton, ToggleSwitch } from '@rocket.chat/fuselage'; +import { useEndpoint, useUserPreference, useTranslation } from '@rocket.chat/ui-contexts'; +import React, { useCallback } from 'react'; + +import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; + +export const useViewModeItems = (): GenericMenuItemProps[] => { + const t = useTranslation(); + + const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); + + const useHandleChange = (value: 'medium' | 'extended' | 'condensed'): (() => void) => + useCallback(() => saveUserPreferences({ data: { sidebarViewMode: value } }), [value]); + + const sidebarViewMode = useUserPreference<'medium' | 'extended' | 'condensed'>('sidebarViewMode', 'extended'); + const sidebarDisplayAvatar = useUserPreference('sidebarDisplayAvatar', false); + + const setToExtended = useHandleChange('extended'); + const setToMedium = useHandleChange('medium'); + const setToCondensed = useHandleChange('condensed'); + + const handleChangeSidebarDisplayAvatar = useCallback( + () => saveUserPreferences({ data: { sidebarDisplayAvatar: !sidebarDisplayAvatar } }), + [saveUserPreferences, sidebarDisplayAvatar], + ); + + return [ + { + id: 'extended', + content: t('Extended'), + icon: 'extended-view', + addon: <RadioButton mi='x16' onChange={setToExtended} checked={sidebarViewMode === 'extended'} />, + }, + { + id: 'medium', + content: t('Medium'), + icon: 'medium-view', + addon: <RadioButton mi='x16' onChange={setToMedium} checked={sidebarViewMode === 'medium'} />, + }, + { + id: 'condensed', + content: t('Condensed'), + icon: 'condensed-view', + addon: <RadioButton mi='x16' onChange={setToCondensed} checked={sidebarViewMode === 'condensed'} />, + }, + { + id: 'avatars', + content: t('Avatars'), + icon: 'user-rounded', + addon: <ToggleSwitch mie='x16' onChange={handleChangeSidebarDisplayAvatar} checked={sidebarDisplayAvatar} />, + }, + ]; +}; diff --git a/apps/meteor/client/sidebar/header/hooks/useCreateRoomModal.tsx b/apps/meteor/client/sidebar/header/hooks/useCreateRoomModal.tsx index 8e14f04ddbc7..2b371ec1b0ef 100644 --- a/apps/meteor/client/sidebar/header/hooks/useCreateRoomModal.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useCreateRoomModal.tsx @@ -1,14 +1,12 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal } from '@rocket.chat/ui-contexts'; -import type { MouseEvent, FC } from 'react'; +import type { FC } from 'react'; import React from 'react'; -export const useCreateRoomModal = (Component: FC<any>): ((e: MouseEvent<HTMLElement>) => void) => { +export const useCreateRoomModal = (Component: FC<any>): (() => void) => { const setModal = useSetModal(); - return useMutableCallback((e) => { - e.preventDefault(); - + return useMutableCallback(() => { const handleClose = (): void => { setModal(null); }; diff --git a/apps/meteor/tests/e2e/administration-menu.spec.ts b/apps/meteor/tests/e2e/administration-menu.spec.ts index 755862a3ef80..4e630c60e16e 100644 --- a/apps/meteor/tests/e2e/administration-menu.spec.ts +++ b/apps/meteor/tests/e2e/administration-menu.spec.ts @@ -16,7 +16,7 @@ test.describe.serial('administration-menu', () => { test('expect open upgrade page', async ({ page }) => { test.skip(IS_EE, 'Community Only'); - await poHomeDiscussion.sidenav.openAdministrationByLabel('Go fully featured'); + await poHomeDiscussion.sidenav.openAdministrationByLabel('Go fully featured ⚡'); await expect(page).toHaveURL('admin/upgrade/go-fully-featured'); }); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts index 348c9fc44554..8b721895cc5c 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts @@ -41,7 +41,7 @@ export class HomeSidenav { async openAdministrationByLabel(text: string): Promise<void> { await this.page.locator('role=button[name="Administration"]').click(); - await this.page.locator(`li.rcx-option >> text="${text}"`).click(); + await this.page.locator(`role=menuitem[name="${text}"]`).click(); } async openInstalledApps(): Promise<void> { @@ -50,8 +50,8 @@ export class HomeSidenav { } async openNewByLabel(text: string): Promise<void> { - await this.page.locator('[data-qa="sidebar-create"]').click(); - await this.page.locator(`li.rcx-option >> text="${text}"`).click(); + await this.page.locator('role=button[name="Create new"]').click(); + await this.page.locator(`role=menuitem[name="${text}"]`).click(); } async logout(): Promise<void> { diff --git a/apps/meteor/tests/unit/client/sidebar/header/actions/Administration.spec.tsx b/apps/meteor/tests/unit/client/sidebar/header/actions/Administration.spec.tsx deleted file mode 100644 index 918d90d2c5cd..000000000000 --- a/apps/meteor/tests/unit/client/sidebar/header/actions/Administration.spec.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { expect, spy } from 'chai'; -import proxyquire from 'proxyquire'; -import type { ReactElement } from 'react'; -import React, { Fragment } from 'react'; - -const COMPONENT_PATH = '../../../../../../client/sidebar/header/actions/Administration'; -const defaultConfig = { - '@rocket.chat/ui-contexts': { - useAtLeastOnePermission: () => true, - usePermission: () => false, - }, - '@rocket.chat/fuselage-hooks': { - useMutableCallback: (cb: () => null) => cb(), - }, - '../../../../ee/client/hooks/useHasLicenseModule': { - useHasLicenseModule: () => true, - }, - '../../../../app/ui-utils/client': { - AccountBox: { - getItems: () => [], - }, - }, - '../../../../app/ui-utils/client/lib/AccountBox': { - AccountBoxItem: {}, - isAppAccountBoxItem: () => false, - }, - '../../../hooks/useReactiveValue': { - useReactiveValue: (item: unknown) => item, - }, - '../../../components/AdministrationList/AdministrationModelList': () => <p>Administration Model List</p>, - '../../../components/AdministrationList/AppsModelList': () => <p>Apps Model List</p>, - '../../../components/AdministrationList/AuditModelList': () => <p>Audit Model List</p>, - '../hooks/useDropdownVisibility': { - useDropdownVisibility: () => ({ - isVisible: true, - toggle: () => null, - }), - }, - '../../../components/AdministrationList/AdministrationList': ({ optionsList }: { optionsList: ReactElement[] }) => - optionsList?.map((i, j) => <Fragment key={j}>{i}</Fragment>), -}; - -describe('sidebar/header/actions/Administration', () => { - it('should render all model list', async () => { - const Administration = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '@rocket.chat/ui-contexts': { - useAtLeastOnePermission: () => true, - usePermission: () => true, - }, - }).default; - - render(<Administration />); - - expect(screen.getByText('Administration Model List')).to.exist; - expect(screen.getByText('Apps Model List')).to.exist; - expect(screen.getByText('Audit Model List')).to.exist; - }); - - it('should not render administration list when isVisible false', async () => { - const Administration = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '../hooks/useDropdownVisibility': { - useDropdownVisibility: () => ({ - isVisible: false, - toggle: () => null, - }), - }, - }).default; - - render(<Administration />); - - expect(screen.getByRole('button')).to.exist; - expect(screen.queryByText('Administration Model List')).to.not.exist; - }); - - it('should render button if has accountBoxItem', async () => { - const Administration = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '../../../../app/ui-utils/client': { - AccountBox: { - getItems: () => [{}], - }, - }, - '@rocket.chat/ui-contexts': { - useAtLeastOnePermission: () => false, - usePermission: () => false, - }, - }).default; - - render(<Administration />); - - expect(screen.getByRole('button')).to.exist; - expect(screen.getByText('Administration Model List')).to.exist; - }); - - it('should not render button if does not have permission', async () => { - const Administration = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '@rocket.chat/ui-contexts': { - useAtLeastOnePermission: () => false, - usePermission: () => false, - }, - '../../../../app/ui-utils/client/lib/AccountBox': { - AccountBoxItem: {}, - isAppAccountBoxItem: () => false, - }, - '../../../../app/ui-utils/client': { - AccountBox: { - getItems: () => [{}], - }, - }, - }).default; - - render(<Administration />); - - expect(screen.queryByText('button')).to.not.exist; - expect(screen.getByText('Administration Model List')).to.exist; - }); - - it('should render administration model list when has account box item with no permissions', async () => { - const Administration = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '@rocket.chat/ui-contexts': { - useAtLeastOnePermission: () => false, - usePermission: () => false, - }, - '../../../../app/ui-utils/client/lib/AccountBox': { - AccountBoxItem: {}, - isAppAccountBoxItem: () => false, - }, - '../../../../app/ui-utils/client': { - AccountBox: { - getItems: () => [{}], - }, - }, - }).default; - - render(<Administration />); - - expect(screen.getByText('Administration Model List')).to.exist; - expect(screen.queryByText('Apps Model List')).to.not.exist; - expect(screen.queryByText('Audit Model List')).to.not.exist; - }); - - it('should render apps model list when has app account box item and no permissions', async () => { - const Administration = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '@rocket.chat/ui-contexts': { - useAtLeastOnePermission: () => false, - usePermission: () => false, - }, - '../../../../app/ui-utils/client/lib/AccountBox': { - AccountBoxItem: {}, - isAppAccountBoxItem: () => true, - }, - '../../../../app/ui-utils/client': { - AccountBox: { - getItems: () => [{}], - }, - }, - }).default; - - render(<Administration />); - - expect(screen.getByText('Apps Model List')).to.exist; - expect(screen.queryByText('Administration Model List')).to.not.exist; - expect(screen.queryByText('Audit Model List')).to.not.exist; - }); - - context('when clicked', () => { - it('should toggle dropdown', async () => { - const toggle = spy(); - const Administration = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '../hooks/useDropdownVisibility': { - useDropdownVisibility: () => ({ - isVisible: true, - toggle, - }), - }, - }).default; - - render(<Administration />); - - userEvent.click(screen.getByRole('button')); - await waitFor(() => expect(toggle).to.have.been.called()); - }); - }); -}); diff --git a/ee/packages/ui-theming/src/paletteDark.ts b/ee/packages/ui-theming/src/paletteDark.ts index 84b44b8818ce..80a29c07cd2d 100644 --- a/ee/packages/ui-theming/src/paletteDark.ts +++ b/ee/packages/ui-theming/src/paletteDark.ts @@ -110,7 +110,7 @@ export const palette = [ category: 'Elevation', description: 'Elevation border and shadow levels', list: [ - { name: 'shadow-elevation-border', token: '', color: '#EBECEF' }, + { name: 'shadow-elevation-border', token: '', color: '#2F343D' }, { name: 'shadow-elevation-1', token: '', color: 'rgba(9, 9, 9, 0.35)' }, { name: 'shadow-elevation-2x', token: '', color: 'rgba(9, 9, 9, 0.3)' }, { name: 'shadow-elevation-2y', token: '', color: 'rgba(9, 9, 9, 0.45)' }, diff --git a/yarn.lock b/yarn.lock index 210c2672084b..89e8ef56d15b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9392,7 +9392,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.12, @rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.143": +"@rocket.chat/css-in-js@npm:^0.31.12, @rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.146": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9418,7 +9418,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.143": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.146": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9647,10 +9647,10 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.319": - version: 0.32.0-dev.319 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.319" - checksum: 2bc25081e2efd632130d3ae0aecb21744e354fa7299bf283a41aa8fd564e2238508f4aac0958ad33de810c4df328b77aa10755ad2c05692cdcb6c70f417146be +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.322": + version: 0.32.0-dev.322 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.322" + checksum: 95a8319aaa4f6fd429c62a594b4094136ec57c519201140ca029bbe44d49cb3fd381fb67d7e20f1341308868794dbc9609dad180c4ab38ddcbd89eea3e78451a languageName: node linkType: hard @@ -9710,14 +9710,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.369 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.369" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.143 - "@rocket.chat/css-supports": ~0.31.23-dev.143 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.319 - "@rocket.chat/memo": ~0.31.23-dev.143 - "@rocket.chat/styled": ~0.31.23-dev.143 + version: 0.32.0-dev.372 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.372" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.146 + "@rocket.chat/css-supports": ~0.31.23-dev.146 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.322 + "@rocket.chat/memo": ~0.31.23-dev.146 + "@rocket.chat/styled": ~0.31.23-dev.146 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9729,7 +9729,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 109d05662debb9dbe38b218d8585953ed4c88b02f3ef7b9c529813e978a93c4af345602078ce237a03d8666d6ce09f91e7b72d2cea1cd4fc5101125b809e5a81 + checksum: 47cdb72c37ee36ebc1dd3daeb8d03950b674dbc3637918dc3d8f6aeead7a56a34db6767e3514e62fc0a9d6a811ef0325cc17c8ffc79532b87f720559ef940ce0 languageName: node linkType: hard @@ -9957,7 +9957,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.143": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.146": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -10762,7 +10762,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.143": +"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.146": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: From 094f3ba0d5f4cbd4eb9a8129ef96667d05a05427 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Wed, 28 Jun 2023 16:39:34 -0300 Subject: [PATCH 020/149] regression: Apps translations not working randomly (#29659) --- .../ui-message/client/actionButtons/messageAction.ts | 3 +-- .../app/ui-message/client/actionButtons/messageBox.ts | 4 +--- .../app/ui-message/client/actionButtons/tabbar.ts | 3 +-- apps/meteor/client/providers/TranslationProvider.tsx | 11 +++++++++++ apps/meteor/ee/client/apps/orchestrator.ts | 9 --------- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts b/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts index e0a0d94a3f4d..29a45ded6a1d 100644 --- a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts +++ b/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts @@ -3,7 +3,6 @@ import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; import { Utilities } from '../../../../ee/lib/misc/Utilities'; import { MessageAction } from '../../../ui-utils/client'; import { messageArgs } from '../../../../client/lib/utils/messageArgs'; -import { t } from '../../../utils/lib/i18n'; import { triggerActionButtonAction } from '../ActionManager'; import { applyButtonFilters } from './lib/applyButtonFilters'; @@ -14,7 +13,7 @@ export const onAdded = (button: IUIActionButton): void => MessageAction.addButton({ id: getIdForActionButton(button), icon: '' as any, - label: t(Utilities.getI18nKeyForApp(button.labelI18n, button.appId)) as any, + label: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), context: button.when?.messageActionContext || ['message', 'message-mobile', 'threads', 'starred'], condition({ room }) { return applyButtonFilters(button, room); diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts b/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts index 828c6bcaf577..d07ecbc23ad8 100644 --- a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts +++ b/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts @@ -1,11 +1,9 @@ import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { ChatRoom } from '../../../models/client'; import { messageBox } from '../../../ui-utils/client'; import { applyButtonFilters } from './lib/applyButtonFilters'; import { triggerActionButtonAction } from '../ActionManager'; -import { t } from '../../../utils/lib/i18n'; import { Utilities } from '../../../../ee/lib/misc/Utilities'; import { RoomManager } from '../../../../client/lib/RoomManager'; import { asReactiveSource } from '../../../../client/lib/tracker'; @@ -14,7 +12,7 @@ const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => ` export const onAdded = (button: IUIActionButton): void => // eslint-disable-next-line no-void - void messageBox.actions.add('Apps', t(Utilities.getI18nKeyForApp(button.labelI18n, button.appId)) as TranslationKey, { + void messageBox.actions.add('Apps', Utilities.getI18nKeyForApp(button.labelI18n, button.appId), { id: getIdForActionButton(button), // icon: button.icon || '', condition() { diff --git a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts index 1ff5a3809aeb..26352cd9b59a 100644 --- a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts +++ b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts @@ -2,7 +2,6 @@ import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; import { addAction, deleteAction } from '../../../../client/views/room/lib/Toolbox'; import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { t } from '../../../utils/lib/i18n'; import { triggerActionButtonAction } from '../ActionManager'; import { applyButtonFilters } from './lib/applyButtonFilters'; @@ -16,7 +15,7 @@ export const onAdded = (button: IUIActionButton): void => id: button.actionId, icon: undefined, // Apps won't provide icons for now order: 300, // Make sure the button only shows up inside the room toolbox - title: t(Utilities.getI18nKeyForApp(button.labelI18n, button.appId)) as any, + title: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), // Filters were applied in the applyButtonFilters function // if the code made it this far, the button should be shown groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], diff --git a/apps/meteor/client/providers/TranslationProvider.tsx b/apps/meteor/client/providers/TranslationProvider.tsx index 36b7c8d99845..122579c8e7bb 100644 --- a/apps/meteor/client/providers/TranslationProvider.tsx +++ b/apps/meteor/client/providers/TranslationProvider.tsx @@ -11,7 +11,9 @@ import type { ReactElement, ReactNode } from 'react'; import React, { useEffect, useMemo } from 'react'; import { I18nextProvider, initReactI18next, useTranslation } from 'react-i18next'; +import { CachedCollectionManager } from '../../app/ui-cached-collection/client'; import { i18n, addSprinfToI18n } from '../../app/utils/lib/i18n'; +import { AppClientOrchestratorInstance } from '../../ee/client/apps/orchestrator'; import { applyCustomTranslations } from '../lib/utils/applyCustomTranslations'; import { filterLanguage } from '../lib/utils/filterLanguage'; import { isRTLScriptLanguage } from '../lib/utils/isRTLScriptLanguage'; @@ -218,6 +220,15 @@ const TranslationProvider = ({ children }: TranslationProviderProps): ReactEleme }); }, [language, loadLocale, availableLanguages]); + useEffect(() => { + const cb = () => { + AppClientOrchestratorInstance.getAppClientManager().initialize(); + AppClientOrchestratorInstance.load(); + }; + CachedCollectionManager.onLogin(cb); + return () => CachedCollectionManager.off('login', cb); + }, []); + return ( <I18nextProvider i18n={i18nextInstance}> <TranslationProviderInner children={children} availableLanguages={availableLanguages} /> diff --git a/apps/meteor/ee/client/apps/orchestrator.ts b/apps/meteor/ee/client/apps/orchestrator.ts index 80a8e2cbf6ef..41efc3e6ed09 100644 --- a/apps/meteor/ee/client/apps/orchestrator.ts +++ b/apps/meteor/ee/client/apps/orchestrator.ts @@ -5,10 +5,8 @@ import type { IPermission } from '@rocket.chat/apps-engine/definition/permission import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage/IAppStorageItem'; import type { AppScreenshot, AppRequestFilter, Serialized, AppRequestsStats, PaginatedAppRequests } from '@rocket.chat/core-typings'; -import { Meteor } from 'meteor/meteor'; import { hasAtLeastOnePermission } from '../../../app/authorization/client'; -import { CachedCollectionManager } from '../../../app/ui-cached-collection/client'; import { sdk } from '../../../app/utils/client/lib/SDKClient'; import { dispatchToastMessage } from '../../../client/lib/toast'; import type { App } from '../../../client/views/marketplace/types'; @@ -263,10 +261,3 @@ class AppClientOrchestrator { } export const AppClientOrchestratorInstance = new AppClientOrchestrator(); - -Meteor.startup(() => { - CachedCollectionManager.onLogin(() => { - AppClientOrchestratorInstance.getAppClientManager().initialize(); - AppClientOrchestratorInstance.load(); - }); -}); From 674f95cca9a0c63463c03b6994358cb298f46061 Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Thu, 29 Jun 2023 06:56:05 -0600 Subject: [PATCH 021/149] fix: Dont update a user's livechat status when its status is offline (#29589) Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> --- .changeset/thin-hats-jog.md | 5 +++++ .../server/business-hour/AbstractBusinessHour.ts | 6 +++++- apps/meteor/app/livechat/server/business-hour/index.ts | 10 +++++----- apps/meteor/ee/server/startup/presence.ts | 5 +++-- apps/meteor/server/models/raw/Users.js | 1 + 5 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 .changeset/thin-hats-jog.md diff --git a/.changeset/thin-hats-jog.md b/.changeset/thin-hats-jog.md new file mode 100644 index 000000000000..2cea8a2312b3 --- /dev/null +++ b/.changeset/thin-hats-jog.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Avoid updating a user's livechat status on login when its status is set to offline diff --git a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts index 7c0ddd007d15..2bafd2f0fa20 100644 --- a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts +++ b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts @@ -44,11 +44,15 @@ export abstract class AbstractBusinessHourBehavior { return this.UsersRepository.isAgentWithinBusinessHours(agentId); } + // After logout, users are turned not-available by default + // This will turn them available unless they put themselves offline (manual status change) async changeAgentActiveStatus(agentId: string, status: string): Promise<any> { return this.UsersRepository.setLivechatStatusIf( agentId, status, - { livechatStatusSystemModified: true }, + // Why this works: statusDefault is the property set when a user manually changes their status + // So if it's set to offline, we can be sure the user will be offline after login and we can skip the update + { livechatStatusSystemModified: true, statusDefault: { $ne: 'offline' } }, { livechatStatusSystemModified: true }, ); } diff --git a/apps/meteor/app/livechat/server/business-hour/index.ts b/apps/meteor/app/livechat/server/business-hour/index.ts index 02fd2480dce6..8b80e415f7a7 100644 --- a/apps/meteor/app/livechat/server/business-hour/index.ts +++ b/apps/meteor/app/livechat/server/business-hour/index.ts @@ -1,6 +1,7 @@ import { Meteor } from 'meteor/meteor'; -import { Accounts } from 'meteor/accounts-base'; import { cronJobs } from '@rocket.chat/cron'; +import type { IUser } from '@rocket.chat/core-typings'; +import { Accounts } from 'meteor/accounts-base'; import { BusinessHourManager } from './BusinessHourManager'; import { SingleBusinessHourBehavior } from './Single'; @@ -16,8 +17,7 @@ Meteor.startup(async () => { businessHourManager.registerBusinessHourBehavior(new BusinessHourBehaviorClass()); businessHourManager.registerBusinessHourType(new DefaultBusinessHour()); - Accounts.onLogin( - async ({ user }: { user: any }) => - user?.roles?.includes('livechat-agent') && !user?.roles?.includes('bot') && businessHourManager.onLogin(user._id), - ); + Accounts.onLogin((user: IUser) => { + void (user?.roles?.includes('livechat-agent') && !user?.roles?.includes('bot') && businessHourManager.onLogin(user._id)); + }); }); diff --git a/apps/meteor/ee/server/startup/presence.ts b/apps/meteor/ee/server/startup/presence.ts index 952539ff7007..51cff87cebeb 100644 --- a/apps/meteor/ee/server/startup/presence.ts +++ b/apps/meteor/ee/server/startup/presence.ts @@ -33,9 +33,10 @@ Meteor.startup(function () { if (login.type !== 'resume') { return; } - void Presence.newConnection(login.user._id, login.connection.id, nodeId).then(() => { + void (async function () { + await Presence.newConnection(login.user._id, login.connection.id, nodeId); updateConns(); - }); + })(); }); Accounts.onLogout(function (login: any): void { diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index afcca73f81e8..f43a1522a548 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -968,6 +968,7 @@ export class UsersRaw extends BaseRaw { setLivechatStatusActiveBasedOnBusinessHours(userId) { const query = { _id: userId, + statusDefault: { $ne: 'offline' }, openBusinessHours: { $exists: true, $not: { $size: 0 }, From 9efc081772142d9ff872b7ea23dfd3b997450bcd Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:28:53 +0530 Subject: [PATCH 022/149] fix: close button of Invite friends Dialog in the Game Center (#28986) --- .../ee/client/apps/gameCenter/GameCenterInvitePlayersModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/ee/client/apps/gameCenter/GameCenterInvitePlayersModal.tsx b/apps/meteor/ee/client/apps/gameCenter/GameCenterInvitePlayersModal.tsx index e5902144dc51..2a03f321d5d6 100644 --- a/apps/meteor/ee/client/apps/gameCenter/GameCenterInvitePlayersModal.tsx +++ b/apps/meteor/ee/client/apps/gameCenter/GameCenterInvitePlayersModal.tsx @@ -54,7 +54,7 @@ const GameCenterInvitePlayersModal = ({ game, onClose }: IGameCenterInvitePlayer return ( <> - <GenericModal onConfirm={sendInvite} title={t('Apps_Game_Center_Invite_Friends')}> + <GenericModal onClose={onClose} onCancel={onClose} onConfirm={sendInvite} title={t('Apps_Game_Center_Invite_Friends')}> <Box mbe='x16'>{t('Invite_Users')}</Box> <Box mbe='x16' display='flex' justifyContent='stretch'> <UserAutoCompleteMultipleFederated value={users} onChange={setUsers} /> From ce99be6b0a7dc2406bc41e1aae24334d1e1a7332 Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Thu, 29 Jun 2023 07:00:06 -0600 Subject: [PATCH 023/149] fix: Omnichannel queue not running for all queues (#29290) Co-authored-by: murtaza98 <murtaza.patrawala@rocket.chat> --- .changeset/spicy-kiwis-invent.md | 5 +++++ apps/meteor/app/livechat/server/lib/RoutingManager.ts | 2 ++ apps/meteor/server/models/raw/LivechatInquiry.ts | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/spicy-kiwis-invent.md diff --git a/.changeset/spicy-kiwis-invent.md b/.changeset/spicy-kiwis-invent.md new file mode 100644 index 000000000000..6cdf5372d52d --- /dev/null +++ b/.changeset/spicy-kiwis-invent.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Omnichannel queue not running for all queues diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.ts b/apps/meteor/app/livechat/server/lib/RoutingManager.ts index aed24ad928dd..55c0bff01c66 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.ts +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.ts @@ -128,6 +128,8 @@ export const RoutingManager: Routing = { if (!agent) { logger.debug(`No agents available. Unable to delegate inquiry ${inquiry._id}`); + // When an inqury reaches here on CE, it will stay here as 'ready' since on CE there's no mechanism to re queue it. + // When reaching this point, managers have to manually transfer the inquiry to another room. This is expected. return LivechatRooms.findOneById(rid); } diff --git a/apps/meteor/server/models/raw/LivechatInquiry.ts b/apps/meteor/server/models/raw/LivechatInquiry.ts index 9bdf7e27b109..2515aef2082d 100644 --- a/apps/meteor/server/models/raw/LivechatInquiry.ts +++ b/apps/meteor/server/models/raw/LivechatInquiry.ts @@ -140,7 +140,7 @@ export class LivechatInquiryRaw extends BaseRaw<ILivechatInquiryRecord> implemen const result = await this.col.findOneAndUpdate( { status: LivechatInquiryStatus.QUEUED, - ...(department && { department }), + ...(department ? { department } : { department: { $exists: false } }), $or: [ { locked: true, From f5507ffd33f1a78d0657dba79e41a52100c6936b Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Thu, 29 Jun 2023 10:01:35 -0300 Subject: [PATCH 024/149] regression: `showThreadsInMainChannel` preference misaligned (#29669) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../preferences/PreferencesMessagesSection.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/meteor/client/views/account/preferences/PreferencesMessagesSection.tsx b/apps/meteor/client/views/account/preferences/PreferencesMessagesSection.tsx index 25f143772be6..76aef16dfcd5 100644 --- a/apps/meteor/client/views/account/preferences/PreferencesMessagesSection.tsx +++ b/apps/meteor/client/views/account/preferences/PreferencesMessagesSection.tsx @@ -1,5 +1,5 @@ import type { SelectOption } from '@rocket.chat/fuselage'; -import { Accordion, Field, Select, FieldGroup, ToggleSwitch } from '@rocket.chat/fuselage'; +import { Accordion, Field, Select, FieldGroup, ToggleSwitch, Box } from '@rocket.chat/fuselage'; import { useUserPreference, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; @@ -127,11 +127,13 @@ const PreferencesMessagesSection = ({ onChange, commitRef, ...props }: FormSecti )} {useMemo( () => ( - <Field display='flex' flexDirection='row' justifyContent='spaceBetween' flexGrow={1}> - <Field.Label>{t('Always_show_thread_replies_in_main_channel')}</Field.Label> - <Field.Row> - <ToggleSwitch checked={showThreadsInMainChannel} onChange={handleShowThreadsInMainChannel} /> - </Field.Row> + <Field> + <Box display='flex' flexDirection='row' justifyContent='spaceBetween' flexGrow={1}> + <Field.Label>{t('Always_show_thread_replies_in_main_channel')}</Field.Label> + <Field.Row> + <ToggleSwitch checked={showThreadsInMainChannel} onChange={handleShowThreadsInMainChannel} /> + </Field.Row> + </Box> <Field.Hint>{t('Accounts_Default_User_Preferences_showThreadsInMainChannel_Description')}</Field.Hint> </Field> ), From 5688ff6c83a68997784877e22925c56acd856ae2 Mon Sep 17 00:00:00 2001 From: Swapnil Bankar <swapnilbankar1010@gmail.com> Date: Thu, 29 Jun 2023 18:43:44 +0530 Subject: [PATCH 025/149] fix: Misaligned alignment of prompt when hover on user / room name in room information and user information section (#28627) Co-authored-by: Hugo Costa <hugocarreiracosta@gmail.com> Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx index bab4bf530c1d..298a01bd2045 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx @@ -10,9 +10,9 @@ type InfoPanelTitleProps = { const isValidIcon = (icon: ReactNode): icon is ComponentProps<typeof Icon>['name'] => typeof icon === 'string'; const InfoPanelTitle: FC<InfoPanelTitleProps> = ({ title, icon }) => ( - <Box display='flex' title={title} flexShrink={0} alignItems='center' fontScale='h4' color='default' withTruncatedText> + <Box display='flex' flexShrink={0} alignItems='center' fontScale='h4' color='default' withTruncatedText> {isValidIcon(icon) ? <Icon name={icon} size='x22' /> : icon} - <Box mis='x8' flexGrow={1} withTruncatedText> + <Box mis='x8' withTruncatedText title={title}> {title} </Box> </Box> From c92b606d33fe301657e78993a2195f93d795ee23 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Thu, 29 Jun 2023 10:58:19 -0300 Subject: [PATCH 026/149] regression: Opening the dial pad crashes the GUI (#29670) --- apps/meteor/client/hooks/useDialModal.tsx | 4 +++- apps/meteor/ee/client/voip/modal/DialPad/hooks/useDialPad.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/hooks/useDialModal.tsx b/apps/meteor/client/hooks/useDialModal.tsx index ac7ad1e81514..ead4c9c070c7 100644 --- a/apps/meteor/client/hooks/useDialModal.tsx +++ b/apps/meteor/client/hooks/useDialModal.tsx @@ -31,7 +31,9 @@ export const useDialModal = (): DialModalControls => { } setModal( - <Suspense fallback={null}> + // TODO: Revisit Modal's FocusScope which currently does not accept null as children. + // Added dummy div fallback for that reason. + <Suspense fallback={<div />}> <DialPadModal initialValue={initialValue} errorMessage={errorMessage} handleClose={closeDialModal} /> </Suspense>, ); diff --git a/apps/meteor/ee/client/voip/modal/DialPad/hooks/useDialPad.tsx b/apps/meteor/ee/client/voip/modal/DialPad/hooks/useDialPad.tsx index 7f10e1d08ee0..ed28ca24fa39 100644 --- a/apps/meteor/ee/client/voip/modal/DialPad/hooks/useDialPad.tsx +++ b/apps/meteor/ee/client/voip/modal/DialPad/hooks/useDialPad.tsx @@ -45,7 +45,7 @@ export const useDialPad = ({ initialValue, initialErrorMessage }: DialPadProps): const { ref, onChange } = register('PhoneInput'); - const value = watch('PhoneInput'); + const value = watch('PhoneInput', ''); const [disabled, setDisabled] = useState(true); From 90038e4316dc2f0a7cade14c5079fc2264e0fe2e Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Thu, 29 Jun 2023 11:12:19 -0600 Subject: [PATCH 027/149] ci: Add missing `context` property on compose file (#29676) --- docker-compose-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index ab54ed2d57d2..e859772f007b 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -29,6 +29,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/authorization-service/Dockerfile + context: . args: SERVICE: authorization-service image: ghcr.io/${LOWERCASE_REPOSITORY}/authorization-service:${DOCKER_TAG} @@ -45,6 +46,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/account-service/Dockerfile + context: . args: SERVICE: account-service image: ghcr.io/${LOWERCASE_REPOSITORY}/account-service:${DOCKER_TAG} @@ -61,6 +63,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/presence-service/Dockerfile + context: . args: SERVICE: presence-service image: ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${DOCKER_TAG} @@ -77,6 +80,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/ddp-streamer/Dockerfile + context: . args: SERVICE: ddp-streamer image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG} @@ -99,6 +103,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/stream-hub-service/Dockerfile + context: . args: SERVICE: stream-hub-service image: ghcr.io/${LOWERCASE_REPOSITORY}/stream-hub-service:${DOCKER_TAG} @@ -115,6 +120,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/queue-worker/Dockerfile + context: . args: SERVICE: queue-worker image: ghcr.io/${LOWERCASE_REPOSITORY}/queue-worker-service:${DOCKER_TAG} @@ -131,6 +137,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/omnichannel-transcript/Dockerfile + context: . args: SERVICE: omnichannel-transcript image: ghcr.io/${LOWERCASE_REPOSITORY}/omnichannel-transcript-service:${DOCKER_TAG} From 2bdddc5615f4826968fa867368acd82c9e6d3969 Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Thu, 29 Jun 2023 12:40:21 -0600 Subject: [PATCH 028/149] regression: `onLogin` hook not destructuring user prop (#29675) --- .changeset/dull-rockets-own.md | 5 +++++ apps/meteor/app/livechat/server/business-hour/index.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/dull-rockets-own.md diff --git a/.changeset/dull-rockets-own.md b/.changeset/dull-rockets-own.md new file mode 100644 index 000000000000..85a61de0c94b --- /dev/null +++ b/.changeset/dull-rockets-own.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +regression: `onLogin` hook not destructuring user prop diff --git a/apps/meteor/app/livechat/server/business-hour/index.ts b/apps/meteor/app/livechat/server/business-hour/index.ts index 8b80e415f7a7..36c67bf74d66 100644 --- a/apps/meteor/app/livechat/server/business-hour/index.ts +++ b/apps/meteor/app/livechat/server/business-hour/index.ts @@ -17,7 +17,7 @@ Meteor.startup(async () => { businessHourManager.registerBusinessHourBehavior(new BusinessHourBehaviorClass()); businessHourManager.registerBusinessHourType(new DefaultBusinessHour()); - Accounts.onLogin((user: IUser) => { + Accounts.onLogin(({ user }: { user: IUser }) => { void (user?.roles?.includes('livechat-agent') && !user?.roles?.includes('bot') && businessHourManager.onLogin(user._id)); }); }); From c31f93ed9677e43d947615c5e2ace233c73df7ad Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Fri, 30 Jun 2023 01:31:44 +0530 Subject: [PATCH 029/149] fix: Newly added agent not following business hours (#29529) Co-authored-by: Kevin Aleman <11577696+KevLehman@users.noreply.github.com> --- .changeset/quick-coats-protect.md | 6 + .../app/lib/server/functions/saveUser.js | 7 +- .../business-hour/AbstractBusinessHour.ts | 34 +++++- .../business-hour/BusinessHourManager.ts | 32 ++--- .../livechat/server/business-hour/Helper.ts | 3 + .../livechat/server/business-hour/Single.ts | 2 + .../livechat/server/hooks/afterUserActions.ts | 50 ++++++-- .../server/hooks/saveAnalyticsData.ts | 2 +- .../app/livechat/server/lib/Livechat.js | 9 +- .../lib/{callbackLogger.ts => logger.ts} | 1 + .../app/livechat/server/sendMessageBySMS.ts | 2 +- apps/meteor/app/livechat/server/startup.ts | 8 +- .../server/business-hour/Helper.ts | 32 +++-- .../server/business-hour/Multiple.ts | 112 ++---------------- .../server/hooks/onCloseLivechat.ts | 2 +- .../server/hooks/resumeOnHold.ts | 2 +- .../app/livechat-enterprise/server/startup.ts | 4 + apps/meteor/lib/callbacks.ts | 2 + apps/meteor/server/models/raw/Users.js | 39 +++--- .../tests/data/livechat/businessHours.ts | 20 +++- apps/meteor/tests/data/livechat/users.ts | 6 + .../api/livechat/19-business-hours.ts | 86 +++++++++++++- packages/core-typings/src/ILivechatAgent.ts | 2 + .../model-typings/src/models/IUsersModel.ts | 10 +- 24 files changed, 301 insertions(+), 172 deletions(-) create mode 100644 .changeset/quick-coats-protect.md rename apps/meteor/app/livechat/server/lib/{callbackLogger.ts => logger.ts} (67%) diff --git a/.changeset/quick-coats-protect.md b/.changeset/quick-coats-protect.md new file mode 100644 index 000000000000..f47a439443eb --- /dev/null +++ b/.changeset/quick-coats-protect.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +fix: newly added agent not following business hours diff --git a/apps/meteor/app/lib/server/functions/saveUser.js b/apps/meteor/app/lib/server/functions/saveUser.js index 849a96260dd2..650a9b05bc31 100644 --- a/apps/meteor/app/lib/server/functions/saveUser.js +++ b/apps/meteor/app/lib/server/functions/saveUser.js @@ -419,11 +419,14 @@ export const saveUser = async function (userId, userData) { await Users.updateOne({ _id: userData._id }, updateUser); - await callbacks.run('afterSaveUser', userData); - // App IPostUserUpdated event hook const userUpdated = await Users.findOneById(userId); + await callbacks.run('afterSaveUser', { + user: userUpdated, + oldUser: oldUserData, + }); + await Apps.triggerEvent(AppEvents.IPostUserUpdated, { user: userUpdated, previousUser: oldUserData, diff --git a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts index 2bafd2f0fa20..80cdc5aea296 100644 --- a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts +++ b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts @@ -1,23 +1,28 @@ import moment from 'moment-timezone'; +import { ILivechatAgentStatus } from '@rocket.chat/core-typings'; import type { ILivechatBusinessHour, ILivechatDepartment } from '@rocket.chat/core-typings'; import type { ILivechatBusinessHoursModel, IUsersModel } from '@rocket.chat/model-typings'; import { LivechatBusinessHours, Users } from '@rocket.chat/models'; import type { UpdateFilter } from 'mongodb'; import type { IWorkHoursCronJobsWrapper } from '../../../../server/models/raw/LivechatBusinessHours'; +import { businessHourLogger } from '../lib/logger'; +import { filterBusinessHoursThatMustBeOpened } from './Helper'; export interface IBusinessHourBehavior { findHoursToCreateJobs(): Promise<IWorkHoursCronJobsWrapper[]>; openBusinessHoursByDayAndHour(day: string, hour: string): Promise<void>; closeBusinessHoursByDayAndHour(day: string, hour: string): Promise<void>; onDisableBusinessHours(): Promise<void>; - onAddAgentToDepartment(options?: Record<string, any>): Promise<any>; + onAddAgentToDepartment(options?: { departmentId: string; agentsId: string[] }): Promise<any>; onRemoveAgentFromDepartment(options?: Record<string, any>): Promise<any>; onRemoveDepartment(department?: ILivechatDepartment): Promise<any>; onStartBusinessHours(): Promise<void>; afterSaveBusinessHours(businessHourData: ILivechatBusinessHour): Promise<void>; allowAgentChangeServiceStatus(agentId: string): Promise<boolean>; changeAgentActiveStatus(agentId: string, status: string): Promise<any>; + // If a new agent is created, this callback will be called + onNewAgentCreated(agentId: string): Promise<void>; } export interface IBusinessHourType { @@ -44,9 +49,7 @@ export abstract class AbstractBusinessHourBehavior { return this.UsersRepository.isAgentWithinBusinessHours(agentId); } - // After logout, users are turned not-available by default - // This will turn them available unless they put themselves offline (manual status change) - async changeAgentActiveStatus(agentId: string, status: string): Promise<any> { + async changeAgentActiveStatus(agentId: string, status: ILivechatAgentStatus): Promise<any> { return this.UsersRepository.setLivechatStatusIf( agentId, status, @@ -56,6 +59,29 @@ export abstract class AbstractBusinessHourBehavior { { livechatStatusSystemModified: true }, ); } + + async onNewAgentCreated(agentId: string): Promise<void> { + businessHourLogger.debug(`Executing onNewAgentCreated for agentId: ${agentId}`); + + const defaultBusinessHour = await LivechatBusinessHours.findOneDefaultBusinessHour(); + if (!defaultBusinessHour) { + businessHourLogger.debug(`No default business hour found for agentId: ${agentId}`); + return; + } + + const businessHourToOpen = await filterBusinessHoursThatMustBeOpened([defaultBusinessHour]); + if (!businessHourToOpen.length) { + businessHourLogger.debug( + `No business hour to open found for agentId: ${agentId}. Default business hour is closed. Setting agentId: ${agentId} to status: ${ILivechatAgentStatus.NOT_AVAILABLE}`, + ); + await Users.setLivechatStatus(agentId, ILivechatAgentStatus.NOT_AVAILABLE); + return; + } + + await Users.addBusinessHourByAgentIds([agentId], defaultBusinessHour._id); + + businessHourLogger.debug(`Setting agentId: ${agentId} to status: ${ILivechatAgentStatus.AVAILABLE}`); + } } export abstract class AbstractBusinessHourType { diff --git a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts index b128fa64485e..7e72a1756358 100644 --- a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts +++ b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts @@ -7,16 +7,7 @@ import { Users } from '@rocket.chat/models'; import type { IBusinessHourBehavior, IBusinessHourType } from './AbstractBusinessHour'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; - -const cronJobDayDict: Record<string, number> = { - Sunday: 0, - Monday: 1, - Tuesday: 2, - Wednesday: 3, - Thursday: 4, - Friday: 5, - Saturday: 6, -}; +import { businessHourLogger } from '../lib/logger'; export class BusinessHourManager { private types: Map<string, IBusinessHourType> = new Map(); @@ -35,6 +26,7 @@ export class BusinessHourManager { async startManager(): Promise<void> { await this.createCronJobsForWorkHours(); + businessHourLogger.debug('Cron jobs created, setting up callbacks'); this.setupCallbacks(); await this.behavior.onStartBusinessHours(); } @@ -115,12 +107,19 @@ export class BusinessHourManager { callbacks.priority.HIGH, 'business-hour-livechat-on-save-agent-department', ); + callbacks.add( + 'livechat.onNewAgentCreated', + this.behavior.onNewAgentCreated.bind(this), + callbacks.priority.HIGH, + 'business-hour-livechat-on-agent-created', + ); } private removeCallbacks(): void { callbacks.remove('livechat.removeAgentDepartment', 'business-hour-livechat-on-remove-agent-department'); callbacks.remove('livechat.afterRemoveDepartment', 'business-hour-livechat-after-remove-department'); callbacks.remove('livechat.saveAgentDepartment', 'business-hour-livechat-on-save-agent-department'); + callbacks.remove('livechat.onNewAgentCreated', 'business-hour-livechat-on-agent-created'); } private async createCronJobsForWorkHours(): Promise<void> { @@ -137,12 +136,17 @@ export class BusinessHourManager { await Promise.all(finish.map(({ day, times }) => this.scheduleCronJob(times, day, 'close', this.closeWorkHoursCallback))); } - private async scheduleCronJob(items: string[], day: string, type: string, job: (day: string, hour: string) => void): Promise<void> { + private async scheduleCronJob( + items: string[], + day: string, + type: 'open' | 'close', + job: (day: string, hour: string) => void, + ): Promise<void> { await Promise.all( items.map((hour) => { - const jobName = `${day}/${hour}/${type}`; - const time = moment(hour, 'HH:mm'); - const scheduleAt = `${time.minutes()} ${time.hours()} * * ${cronJobDayDict[day]}`; + const time = moment(hour, 'HH:mm').day(day); + const jobName = `${time.format('dddd')}/${time.format('HH:mm')}/${type}`; + const scheduleAt = `${time.minutes()} ${time.hours()} * * ${time.day()}`; this.addToCache(jobName); return this.cronJobs.add(jobName, scheduleAt, () => job(day, hour)); }), diff --git a/apps/meteor/app/livechat/server/business-hour/Helper.ts b/apps/meteor/app/livechat/server/business-hour/Helper.ts index ba2b48089afb..ccf0381f0c0c 100644 --- a/apps/meteor/app/livechat/server/business-hour/Helper.ts +++ b/apps/meteor/app/livechat/server/business-hour/Helper.ts @@ -4,6 +4,7 @@ import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import { LivechatBusinessHours, Users } from '@rocket.chat/models'; import { createDefaultBusinessHourRow } from './LivechatBusinessHours'; +import { businessHourLogger } from '../lib/logger'; export const filterBusinessHoursThatMustBeOpened = async ( businessHours: ILivechatBusinessHour[], @@ -52,8 +53,10 @@ export const openBusinessHourDefault = async (): Promise<void> => { }, }); const businessHoursToOpenIds = (await filterBusinessHoursThatMustBeOpened(activeBusinessHours)).map((businessHour) => businessHour._id); + businessHourLogger.debug({ msg: 'Opening default business hours', businessHoursToOpenIds }); await Users.openAgentsBusinessHoursByBusinessHourId(businessHoursToOpenIds); await Users.updateLivechatStatusBasedOnBusinessHours(); + businessHourLogger.debug('Done opening default business hours'); }; export const createDefaultBusinessHourIfNotExists = async (): Promise<void> => { diff --git a/apps/meteor/app/livechat/server/business-hour/Single.ts b/apps/meteor/app/livechat/server/business-hour/Single.ts index fed214305e7f..2996d5f1c2c7 100644 --- a/apps/meteor/app/livechat/server/business-hour/Single.ts +++ b/apps/meteor/app/livechat/server/business-hour/Single.ts @@ -3,6 +3,7 @@ import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import type { IBusinessHourBehavior } from './AbstractBusinessHour'; import { AbstractBusinessHourBehavior } from './AbstractBusinessHour'; import { openBusinessHourDefault } from './Helper'; +import { businessHourLogger } from '../lib/logger'; export class SingleBusinessHourBehavior extends AbstractBusinessHourBehavior implements IBusinessHourBehavior { async openBusinessHoursByDayAndHour(day: string, hour: string): Promise<void> { @@ -25,6 +26,7 @@ export class SingleBusinessHourBehavior extends AbstractBusinessHourBehavior imp } async onStartBusinessHours(): Promise<void> { + businessHourLogger.debug('Starting Single Business Hours'); return openBusinessHourDefault(); } diff --git a/apps/meteor/app/livechat/server/hooks/afterUserActions.ts b/apps/meteor/app/livechat/server/hooks/afterUserActions.ts index 8b47b8af1334..3ec0bd81785e 100644 --- a/apps/meteor/app/livechat/server/hooks/afterUserActions.ts +++ b/apps/meteor/app/livechat/server/hooks/afterUserActions.ts @@ -1,23 +1,51 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { Users } from '@rocket.chat/models'; -import type { UsersUpdateParamsPOST } from '@rocket.chat/rest-typings'; import { callbacks } from '../../../../lib/callbacks'; +import { Livechat } from '../lib/Livechat'; +import { callbackLogger } from '../lib/logger'; -type UserData = UsersUpdateParamsPOST['data'] & { _id: string }; +type IAfterSaveUserProps = { + user: IUser; + oldUser: IUser | null; +}; + +const wasAgent = (user: Pick<IUser, 'roles'> | null) => user?.roles?.includes('livechat-agent'); +const isAgent = (user: Pick<IUser, 'roles'> | null) => user?.roles?.includes('livechat-agent'); + +const handleAgentUpdated = async (userData: IAfterSaveUserProps) => { + const { + user: { _id: userId, username }, + user: newUser, + oldUser, + } = userData; + + if (wasAgent(oldUser) && !isAgent(newUser)) { + callbackLogger.debug('Removing agent', userId); + await Livechat.removeAgent(username); + } -const handleAgentUpdated = async (userData: UserData) => { - if (!userData?.roles?.includes('livechat-agent')) { - await Users.unsetExtension(userData._id); + if (!wasAgent(oldUser) && isAgent(newUser)) { + callbackLogger.debug('Adding agent', userId); + await Livechat.addAgent(username); } }; -const handleDeactivateUser = async (userData: IUser) => { - if (userData?.roles?.includes('livechat-agent')) { - await Users.unsetExtension(userData._id); +const handleDeactivateUser = async (user: IUser) => { + if (wasAgent(user)) { + callbackLogger.debug('Removing agent', user._id); + await Livechat.removeAgent(user.username); } }; -callbacks.add('afterSaveUser', handleAgentUpdated, callbacks.priority.LOW, 'livechat-after-save-user-remove-extension'); +const handleActivateUser = async (user: IUser) => { + if (isAgent(user)) { + callbackLogger.debug('Adding agent', user._id); + await Livechat.addAgent(user.username); + } +}; + +callbacks.add('afterSaveUser', handleAgentUpdated, callbacks.priority.LOW, 'livechat-after-save-user-update-agent'); + +callbacks.add('afterDeactivateUser', handleDeactivateUser, callbacks.priority.LOW, 'livechat-after-deactivate-user-remove-agent'); -callbacks.add('afterDeactivateUser', handleDeactivateUser, callbacks.priority.LOW, 'livechat-after-deactivate-user-remove-extension'); +callbacks.add('afterActivateUser', handleActivateUser, callbacks.priority.LOW, 'livechat-after-activate-user-add-agent'); diff --git a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts index db3dca5f0a84..53a8c180c61b 100644 --- a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts +++ b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts @@ -3,7 +3,7 @@ import { LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; -import { callbackLogger } from '../lib/callbackLogger'; +import { callbackLogger } from '../lib/logger'; callbacks.add( 'afterSaveMessage', diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 6ba4a653b211..1fcf39c30a31 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -533,6 +533,9 @@ export const Livechat = { if (await addUserRolesAsync(user._id, ['livechat-agent'])) { await Users.setOperator(user._id, true); await this.setUserStatusLivechat(user._id, user.status !== 'offline' ? 'available' : 'not-available'); + + callbacks.runAsync('livechat.onNewAgentCreated', user._id); + return user; } @@ -571,14 +574,10 @@ export const Livechat = { const { _id } = user; if (await removeUserFromRolesAsync(_id, ['livechat-agent'])) { - await Users.setOperator(_id, false); - await Users.removeLivechatData(_id); - await this.setUserStatusLivechat(_id, 'not-available'); - await Promise.all([ + Users.removeAgent(_id), LivechatDepartmentAgents.removeByAgentId(_id), LivechatVisitors.removeContactManagerByUsername(username), - Users.unsetExtension(_id), ]); return true; } diff --git a/apps/meteor/app/livechat/server/lib/callbackLogger.ts b/apps/meteor/app/livechat/server/lib/logger.ts similarity index 67% rename from apps/meteor/app/livechat/server/lib/callbackLogger.ts rename to apps/meteor/app/livechat/server/lib/logger.ts index 7b56495bba3c..ee6b2969c5c5 100644 --- a/apps/meteor/app/livechat/server/lib/callbackLogger.ts +++ b/apps/meteor/app/livechat/server/lib/logger.ts @@ -1,3 +1,4 @@ import { Logger } from '../../../../server/lib/logger/Logger'; export const callbackLogger = new Logger('[Omnichannel] Callback'); +export const businessHourLogger = new Logger('Business Hour'); diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.ts b/apps/meteor/app/livechat/server/sendMessageBySMS.ts index 7df67b44b2fb..8449c2c7bacf 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.ts +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.ts @@ -5,7 +5,7 @@ import { OmnichannelIntegration } from '@rocket.chat/core-services'; import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; import { normalizeMessageFileUpload } from '../../utils/server/functions/normalizeMessageFileUpload'; -import { callbackLogger } from './lib/callbackLogger'; +import { callbackLogger } from './lib/logger'; callbacks.add( 'afterSaveMessage', diff --git a/apps/meteor/app/livechat/server/startup.ts b/apps/meteor/app/livechat/server/startup.ts index e268fd7aa6d2..d8c5ba098d65 100644 --- a/apps/meteor/app/livechat/server/startup.ts +++ b/apps/meteor/app/livechat/server/startup.ts @@ -62,10 +62,14 @@ Meteor.startup(async () => { await createDefaultBusinessHourIfNotExists(); settings.watch<boolean>('Livechat_enable_business_hours', async (value) => { + Livechat.logger.debug(`Changing business hour type to ${value}`); if (value) { - return businessHourManager.startManager(); + await businessHourManager.startManager(); + Livechat.logger.debug(`Business hour manager started`); + return; } - return businessHourManager.stopManager(); + await businessHourManager.stopManager(); + Livechat.logger.debug(`Business hour manager stopped`); }); settings.watch<string>('Livechat_Routing_Method', function (value) { diff --git a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts index b2e748356174..6575f13bbde2 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts @@ -4,11 +4,13 @@ import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import { LivechatBusinessHours, LivechatDepartment, LivechatDepartmentAgents, Users } from '@rocket.chat/models'; import { isEnterprise } from '../../../license/server/license'; +import { businessHourLogger } from '../../../../../app/livechat/server/lib/logger'; const getAllAgentIdsWithoutDepartment = async (): Promise<string[]> => { const agentIdsWithDepartment = ( await LivechatDepartmentAgents.find({ departmentEnabled: true }, { projection: { agentId: 1 } }).toArray() - ).map((dept: any) => dept.agentId); + ).map((dept) => dept.agentId); + const agentIdsWithoutDepartment = ( await Users.findUsersInRolesWithQuery( 'livechat-agent', @@ -17,7 +19,8 @@ const getAllAgentIdsWithoutDepartment = async (): Promise<string[]> => { }, { projection: { _id: 1 } }, ).toArray() - ).map((user: any) => user._id); + ).map((user) => user._id); + return agentIdsWithoutDepartment; }; @@ -43,7 +46,7 @@ const getAllAgentIdsForDefaultBusinessHour = async (): Promise<string[]> => { return [...new Set([...withoutDepartment, ...withDepartmentNotConnectedToBusinessHour])]; }; -const getAgentIdsToHandle = async (businessHour: Record<string, any>): Promise<string[]> => { +const getAgentIdsToHandle = async (businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>): Promise<string[]> => { if (businessHour.type === LivechatBusinessHourTypes.DEFAULT) { return getAllAgentIdsForDefaultBusinessHour(); } @@ -51,22 +54,35 @@ const getAgentIdsToHandle = async (businessHour: Record<string, any>): Promise<s await LivechatDepartment.findEnabledByBusinessHourId(businessHour._id, { projection: { _id: 1 }, }).toArray() - ).map((dept: any) => dept._id); + ).map((dept) => dept._id); return ( await LivechatDepartmentAgents.findByDepartmentIds(departmentIds, { projection: { agentId: 1 }, }).toArray() - ).map((dept: any) => dept.agentId); + ).map((dept) => dept.agentId); }; -export const openBusinessHour = async (businessHour: Record<string, any>): Promise<void> => { - const agentIds: string[] = await getAgentIdsToHandle(businessHour); +export const openBusinessHour = async (businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>): Promise<void> => { + const agentIds = await getAgentIdsToHandle(businessHour); + businessHourLogger.debug({ + msg: 'Opening business hour', + businessHour: businessHour._id, + totalAgents: agentIds.length, + top10AgentIds: agentIds.slice(0, 10), + }); + await Users.addBusinessHourByAgentIds(agentIds, businessHour._id); await Users.updateLivechatStatusBasedOnBusinessHours(); }; -export const closeBusinessHour = async (businessHour: Record<string, any>): Promise<void> => { +export const closeBusinessHour = async (businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>): Promise<void> => { const agentIds: string[] = await getAgentIdsToHandle(businessHour); + businessHourLogger.debug({ + msg: 'Closing business hour', + businessHour: businessHour._id, + totalAgents: agentIds.length, + top10AgentIds: agentIds.slice(0, 10), + }); await Users.removeBusinessHourByAgentIds(agentIds, businessHour._id); await Users.updateLivechatStatusBasedOnBusinessHours(); }; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts index 2c0db1516f8f..0fa768453e3a 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts @@ -1,16 +1,12 @@ import moment from 'moment'; -import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import type { ILivechatDepartment, ILivechatBusinessHour } from '@rocket.chat/core-typings'; import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models'; import type { IBusinessHourBehavior } from '../../../../../app/livechat/server/business-hour/AbstractBusinessHour'; import { AbstractBusinessHourBehavior } from '../../../../../app/livechat/server/business-hour/AbstractBusinessHour'; -import { - filterBusinessHoursThatMustBeOpened, - filterBusinessHoursThatMustBeOpenedByDay, -} from '../../../../../app/livechat/server/business-hour/Helper'; +import { filterBusinessHoursThatMustBeOpened } from '../../../../../app/livechat/server/business-hour/Helper'; import { closeBusinessHour, openBusinessHour, removeBusinessHourByAgentIds } from './Helper'; -import { bhLogger } from '../lib/logger'; +import { businessHourLogger } from '../../../../../app/livechat/server/lib/logger'; interface IBusinessHoursExtraProperties extends ILivechatBusinessHour { timezoneName: string; @@ -23,6 +19,7 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior this.onAddAgentToDepartment = this.onAddAgentToDepartment.bind(this); this.onRemoveAgentFromDepartment = this.onRemoveAgentFromDepartment.bind(this); this.onRemoveDepartment = this.onRemoveDepartment.bind(this); + this.onNewAgentCreated = this.onNewAgentCreated.bind(this); } async onStartBusinessHours(): Promise<void> { @@ -39,6 +36,11 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior }, }); const businessHoursToOpen = await filterBusinessHoursThatMustBeOpened(activeBusinessHours); + businessHourLogger.debug({ + msg: 'Starting Multiple Business Hours', + totalBusinessHoursToOpen: businessHoursToOpen.length, + top10BusinessHoursToOpen: businessHoursToOpen.slice(0, 10), + }); for (const businessHour of businessHoursToOpen) { void this.openBusinessHour(businessHour); } @@ -84,7 +86,7 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior return openBusinessHour(businessHour); } - async onAddAgentToDepartment(options: Record<string, any> = {}): Promise<any> { + async onAddAgentToDepartment(options: { departmentId: string; agentsId: string[] }): Promise<any> { const { departmentId, agentsId } = options; const department = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'businessHourId'>>(departmentId, { projection: { businessHourId: 1 }, @@ -132,96 +134,8 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior return this.handleRemoveAgentsFromDepartments(deletedDepartment, agentsIds, options); } - async allowAgentChangeServiceStatus(agentId: string): Promise<boolean> { - const isWithinBushinessHours = await this.UsersRepository.isAgentWithinBusinessHours(agentId); - if (isWithinBushinessHours) { - return true; - } - - bhLogger.debug(`No active business hour found for agent with id: ${agentId} based on user's cache. Attempting to recheck the status`); - - // double check to see if user is actually within business hours - // this is required since the cache of businessHour Ids we maintain within user's collection might be stale - // in many scenario's like, if the agent is created when a business is active, - // or if a normal user is converted to agent when a business hour is active - const currentTime = moment.utc(moment().utc().format('dddd:HH:mm'), 'dddd:HH:mm'); - const day = currentTime.format('dddd'); - const allActiveBusinessHoursForEntireWeek = await this.BusinessHourRepository.findActiveBusinessHours({ - projection: { - workHours: 1, - timezone: 1, - type: 1, - active: 1, - }, - }); - const openedBusinessHours = await filterBusinessHoursThatMustBeOpenedByDay(allActiveBusinessHoursForEntireWeek, day); - if (!openedBusinessHours.length) { - bhLogger.debug(`Business hour status recheck failed for agentId: ${agentId}. No opened business hour found`); - return false; - } - - const agentDepartments = await LivechatDepartmentAgents.find( - { departmentEnabled: true, agentId }, - { projection: { agentId: 1, departmentId: 1 } }, - ).toArray(); - - if (agentDepartments.length) { - // check if any one these departments have a opened business hour linked to it - const departments = await LivechatDepartment.findInIds( - agentDepartments.map(({ departmentId }) => departmentId), - { projection: { _id: 1, businessHourId: 1 } }, - ).toArray(); - - const departmentsWithActiveBH = departments.filter( - ({ businessHourId }) => businessHourId && openedBusinessHours.findIndex(({ _id }) => _id === businessHourId) !== -1, - ); - - if (!departmentsWithActiveBH.length) { - bhLogger.debug( - `No opened business hour found for any of the departments connected to the agent with id: ${agentId}. Now, checking if the default business hour can be used`, - ); - - // check if this agent has any departments that is connected to any non-default business hour - // if no such departments found then check default BH and if it is active, then allow the agent to change service status - const hasAtLeastOneDepartmentWithNonDefaultBH = departments.some(({ businessHourId }) => { - // check if business hour is active - return businessHourId && allActiveBusinessHoursForEntireWeek.findIndex(({ _id }) => _id === businessHourId) !== -1; - }); - if (!hasAtLeastOneDepartmentWithNonDefaultBH) { - const isDefaultBHActive = openedBusinessHours.find(({ type }) => type === LivechatBusinessHourTypes.DEFAULT); - if (isDefaultBHActive?._id) { - await this.UsersRepository.openAgentBusinessHoursByBusinessHourIdsAndAgentId([isDefaultBHActive._id], agentId); - - bhLogger.debug(`Business hour status recheck passed for agentId: ${agentId}. Found default business hour to be active`); - return true; - } - bhLogger.debug(`Business hour status recheck failed for agentId: ${agentId}. Found default business hour to be inactive`); - } - return false; - } - - const activeBusinessHoursForAgent = departmentsWithActiveBH.map(({ businessHourId }) => businessHourId); - await this.UsersRepository.openAgentBusinessHoursByBusinessHourIdsAndAgentId(activeBusinessHoursForAgent, agentId); - - bhLogger.debug( - `Business hour status recheck passed for agentId: ${agentId}. Found following business hours to be active:`, - activeBusinessHoursForAgent, - ); - return true; - } - - // check if default businessHour is active - const isDefaultBHActive = openedBusinessHours.find(({ type }) => type === LivechatBusinessHourTypes.DEFAULT); - if (isDefaultBHActive?._id) { - await this.UsersRepository.openAgentBusinessHoursByBusinessHourIdsAndAgentId([isDefaultBHActive._id], agentId); - - bhLogger.debug(`Business hour status recheck passed for agentId: ${agentId}. Found default business hour to be active`); - return true; - } - - bhLogger.debug(`Business hour status recheck failed for agentId: ${agentId}. No opened business hour found`); - - return false; + allowAgentChangeServiceStatus(agentId: string): Promise<boolean> { + return this.UsersRepository.isAgentWithinBusinessHours(agentId); } private async handleRemoveAgentsFromDepartments(department: Record<string, any>, agentsIds: string[], options: any): Promise<any> { @@ -255,7 +169,7 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior return options; } - private async openBusinessHour(businessHour: Record<string, any>): Promise<void> { + private async openBusinessHour(businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>): Promise<void> { return openBusinessHour(businessHour); } @@ -270,7 +184,7 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior await removeBusinessHourByAgentIds(agentIds, businessHourId); } - private async closeBusinessHour(businessHour: Record<string, any>): Promise<void> { + private async closeBusinessHour(businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>): Promise<void> { await closeBusinessHour(businessHour); } } diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/onCloseLivechat.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/onCloseLivechat.ts index 59d96b3fac1b..3f93967ae9b8 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/onCloseLivechat.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/onCloseLivechat.ts @@ -4,8 +4,8 @@ import { LivechatRooms, Subscriptions } from '@rocket.chat/models'; import { callbacks } from '../../../../../lib/callbacks'; import { settings } from '../../../../../app/settings/server'; import { debouncedDispatchWaitingQueueStatus } from '../lib/Helper'; -import { callbackLogger } from '../../../../../app/livechat/server/lib/callbackLogger'; import { AutoCloseOnHoldScheduler } from '../lib/AutoCloseOnHoldScheduler'; +import { callbackLogger } from '../../../../../app/livechat/server/lib/logger'; type LivechatCloseCallbackParams = { room: IOmnichannelRoom; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts index 8682f03852b1..de6b0861d878 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts @@ -4,8 +4,8 @@ import { LivechatRooms, LivechatVisitors, Users } from '@rocket.chat/models'; import { OmnichannelEEService } from '@rocket.chat/core-services'; import { callbacks } from '../../../../../lib/callbacks'; -import { callbackLogger } from '../../../../../app/livechat/server/lib/callbackLogger'; import { i18n } from '../../../../../server/lib/i18n'; +import { callbackLogger } from '../../../../../app/livechat/server/lib/logger'; const resumeOnHoldCommentAndUser = async (room: IOmnichannelRoom): Promise<{ comment: string; resumedBy: IUser }> => { const { diff --git a/apps/meteor/ee/app/livechat-enterprise/server/startup.ts b/apps/meteor/ee/app/livechat-enterprise/server/startup.ts index 26a7cacce4c4..7ba2479dd2fb 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/startup.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/startup.ts @@ -8,6 +8,7 @@ import { MultipleBusinessHoursBehavior } from './business-hour/Multiple'; import { SingleBusinessHourBehavior } from '../../../../app/livechat/server/business-hour/Single'; import { businessHourManager } from '../../../../app/livechat/server/business-hour'; import { resetDefaultBusinessHourIfNeeded } from './business-hour/Helper'; +import { logger } from './lib/logger'; const visitorActivityMonitor = new VisitorInactivityMonitor(); const businessHours = { @@ -31,12 +32,15 @@ Meteor.startup(async function () { await updatePredictedVisitorAbandonment(); }); settings.change<string>('Livechat_business_hour_type', async (value) => { + logger.debug(`Changing business hour type to ${value}`); if (!Object.keys(businessHours).includes(value)) { + logger.error(`Invalid business hour type ${value}`); return; } businessHourManager.registerBusinessHourBehavior(businessHours[value as keyof typeof businessHours]); if (settings.get('Livechat_enable_business_hours')) { await businessHourManager.startManager(); + logger.debug(`Business hour manager started`); } }); diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 32a4e59a7b4b..f84b927c89e0 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -61,6 +61,7 @@ interface EventLikeCallbackSignatures { 'livechat:afterReturnRoomAsInquiry': (params: { room: IRoom }) => void; 'livechat.setUserStatusLivechat': (params: { userId: IUser['_id']; status: OmnichannelAgentStatus }) => void; 'livechat.agentStatusChanged': (params: { userId: IUser['_id']; status: OmnichannelAgentStatus }) => void; + 'livechat.onNewAgentCreated': (agentId: string) => void; 'livechat.afterTakeInquiry': (inq: InquiryWithAgentInfo, agent: { agentId: string; username: string }) => void; 'afterAddedToRoom': (params: { user: IUser; inviter?: IUser }, room: IRoom) => void; 'beforeAddedToRoom': (params: { user: IUser; inviter: IUser }) => void; @@ -91,6 +92,7 @@ interface EventLikeCallbackSignatures { 'afterValidateLogin': (login: { user: IUser }) => void; 'afterJoinRoom': (user: IUser, room: IRoom) => void; 'beforeCreateRoom': (data: { type: IRoom['t']; extraData: { encrypted: boolean } }) => void; + 'afterSaveUser': ({ user, oldUser }: { user: IUser; oldUser: IUser | null }) => void; } /** diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index f43a1522a548..f8ae05e11d11 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -842,9 +842,6 @@ export class UsersRaw extends BaseRaw { }; const update = { - $set: { - statusLivechat: 'available', - }, $addToSet: { openBusinessHours: { $each: businessHourIds }, }, @@ -878,9 +875,6 @@ export class UsersRaw extends BaseRaw { }; const update = { - $set: { - statusLivechat: 'available', - }, $addToSet: { openBusinessHours: businessHourId, }, @@ -985,15 +979,14 @@ export class UsersRaw extends BaseRaw { } async isAgentWithinBusinessHours(agentId) { - return ( - (await this.find({ - _id: agentId, - openBusinessHours: { - $exists: true, - $not: { $size: 0 }, - }, - }).count()) > 0 - ); + const query = { + _id: agentId, + openBusinessHours: { + $exists: true, + $not: { $size: 0 }, + }, + }; + return (await this.col.countDocuments(query)) > 0; } removeBusinessHoursFromAllUsers() { @@ -2887,4 +2880,20 @@ export class UsersRaw extends BaseRaw { countRoomMembers(roomId) { return this.col.countDocuments({ __rooms: roomId, active: true }); } + + removeAgent(_id) { + const update = { + $set: { + operator: false, + }, + $unset: { + livechat: 1, + statusLivechat: 1, + extension: 1, + openBusinessHours: 1, + }, + }; + + return this.updateOne({ _id }, update); + } } diff --git a/apps/meteor/tests/data/livechat/businessHours.ts b/apps/meteor/tests/data/livechat/businessHours.ts index 59b232a38d22..73ccdf75d096 100644 --- a/apps/meteor/tests/data/livechat/businessHours.ts +++ b/apps/meteor/tests/data/livechat/businessHours.ts @@ -1,7 +1,9 @@ -import { ILivechatBusinessHour } from "@rocket.chat/core-typings"; +import { ILivechatBusinessHour, LivechatBusinessHourTypes } from "@rocket.chat/core-typings"; import { api, credentials, methodCall, request } from "../api-data"; import { updateEESetting, updateSetting } from "../permissions.helper" +import { saveBusinessHour } from "./business-hours"; import moment from "moment"; + type ISaveBhApiWorkHour = Omit<ILivechatBusinessHour, '_id' | 'ts' | 'timezone'> & { workHours: { day: string, start: string, finish: string, open: boolean }[] } & { departmentsToApplyBusinessHour?: string } & { timezoneName: string }; export const makeDefaultBusinessHourActiveAndClosed = async () => { @@ -76,6 +78,22 @@ export const disableDefaultBusinessHour = async () => { }); } +export const getDefaultBusinessHour = async (): Promise<ILivechatBusinessHour> => { + const response = await request.get(api('livechat/business-hour')).set(credentials).query({ type: LivechatBusinessHourTypes.DEFAULT }).expect(200); + return response.body.businessHour; +}; + +export const openOrCloseBusinessHour = async (businessHour: ILivechatBusinessHour, open: boolean) => { + const enabledBusinessHour = { + ...businessHour, + timezoneName: businessHour.timezone.name, + workHours: getWorkHours(open), + departmentsToApplyBusinessHour: businessHour.departments?.map((department) => department._id).join(',') || '', + } + + await saveBusinessHour(enabledBusinessHour as any); +} + export const getWorkHours = (open = true): ISaveBhApiWorkHour['workHours'] => { const workHours: ISaveBhApiWorkHour['workHours'] = []; diff --git a/apps/meteor/tests/data/livechat/users.ts b/apps/meteor/tests/data/livechat/users.ts index 401ba463e575..7a5dc23b4cc0 100644 --- a/apps/meteor/tests/data/livechat/users.ts +++ b/apps/meteor/tests/data/livechat/users.ts @@ -3,6 +3,7 @@ import type { IUser } from "@rocket.chat/core-typings"; import { password } from "../user"; import { createUser, login } from "../users.helper"; import { createAgent, makeAgentAvailable } from "./rooms"; +import { api, credentials, request } from "../api-data"; export const createBotAgent = async (): Promise<{ credentials: { 'X-Auth-Token': string; 'X-User-Id': string; }; @@ -23,3 +24,8 @@ export const createBotAgent = async (): Promise<{ export const getRandomVisitorToken = (): string => faker.string.alphanumeric(17); +export const removeAgent = async (userId: string): Promise<void> => { + await request.delete(api(`livechat/users/agent/${userId}`)) + .set(credentials) + .expect(200); +} 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 eb5393920c2d..d2e41fa11bec 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 @@ -1,14 +1,20 @@ /* eslint-env mocha */ -import { LivechatBusinessHourTypes, LivechatBusinessHourBehaviors } from '@rocket.chat/core-typings'; +import type { ILivechatAgent, ILivechatBusinessHour } from '@rocket.chat/core-typings'; +import { ILivechatAgentStatus, LivechatBusinessHourBehaviors, LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { getCredentials, api, request, credentials } from '../../../data/api-data'; import { saveBusinessHour } from '../../../data/livechat/business-hours'; -import { updatePermission, updateSetting } from '../../../data/permissions.helper'; +import { updateEESetting, updatePermission, updateSetting } from '../../../data/permissions.helper'; import { IS_EE } from '../../../e2e/config/constants'; +import { createUser, deleteUser, getMe, login } from '../../../data/users.helper'; import { createAgent, makeAgentAvailable } from '../../../data/livechat/rooms'; -import { getWorkHours } from '../../../data/livechat/businessHours'; +import { sleep } from '../../../../lib/utils/sleep'; +import { getDefaultBusinessHour, openOrCloseBusinessHour, getWorkHours } from '../../../data/livechat/businessHours'; +import type { IUserCredentialsHeader } from '../../../data/user'; +import { password } from '../../../data/user'; +import { removeAgent } from '../../../data/livechat/users'; describe('[CE] LIVECHAT - business hours', function () { this.retries(0); @@ -226,4 +232,78 @@ describe('[CE] LIVECHAT - business hours', function () { expect(result).to.have.property('error'); }); }); + + describe('BH behavior upon new agent creation/deletion', () => { + let defaultBH: ILivechatBusinessHour; + let agent: ILivechatAgent; + let agentCredentials: IUserCredentialsHeader; + + 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(); + agentCredentials = await login(agent.username, password); + }); + + it('should create a new agent and verify if it is assigned to the default business hour which is open', async () => { + agent = await createAgent(agent.username); + + const latestAgent: ILivechatAgent = await getMe(agentCredentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(defaultBH._id); + }); + + it('should create a new agent and verify if it is assigned to the default business hour which is closed', async () => { + await openOrCloseBusinessHour(defaultBH, false); + + const newUser: ILivechatAgent = await createUser(); + const newUserCredentials = await login(newUser.username, password); + await createAgent(newUser.username); + + const latestAgent: ILivechatAgent = await getMe(newUserCredentials); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.undefined; + expect(latestAgent.statusLivechat).to.be.equal(ILivechatAgentStatus.NOT_AVAILABLE); + }); + + it('should verify if agent is assigned to BH when it is opened', async () => { + // first verify if agent is not assigned to any BH + let latestAgent: ILivechatAgent = await getMe(agentCredentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(0); + expect(latestAgent.statusLivechat).to.be.equal(ILivechatAgentStatus.NOT_AVAILABLE); + + // now open BH + await openOrCloseBusinessHour(defaultBH, true); + + // verify if agent is assigned to BH + latestAgent = await getMe(agentCredentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(defaultBH._id); + + // verify if agent is able to make themselves available + await makeAgentAvailable(agentCredentials as any); + }); + + it('should verify if BH related props are cleared when an agent is deleted', async () => { + await removeAgent(agent._id); + + const latestAgent: ILivechatAgent = await getMe(agentCredentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.undefined; + expect(latestAgent.statusLivechat).to.be.undefined; + }); + + after(async () => { + await deleteUser(agent._id); + }); + }); }); diff --git a/packages/core-typings/src/ILivechatAgent.ts b/packages/core-typings/src/ILivechatAgent.ts index 35e5763e57c6..68a99c2723d9 100644 --- a/packages/core-typings/src/ILivechatAgent.ts +++ b/packages/core-typings/src/ILivechatAgent.ts @@ -13,4 +13,6 @@ export interface ILivechatAgent extends IUser { livechatCount: number; lastRoutingTime: Date; livechatStatusSystemModified?: boolean; + + openBusinessHours?: string[]; } diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index bc279190ea81..5900b5a14091 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -8,6 +8,7 @@ import type { ILoginToken, IPersonalAccessToken, AtLeast, + ILivechatAgentStatus, } from '@rocket.chat/core-typings'; import type { FindPaginated, IBaseModel } from './IBaseModel'; @@ -92,7 +93,7 @@ export interface IUsersModel extends IBaseModel<IUser> { setLastRoutingTime(userId: any): Promise<number>; - setLivechatStatusIf(userId: any, status: any, conditions?: any, extraFields?: any): Promise<UpdateResult>; + setLivechatStatusIf(userId: string, status: ILivechatAgentStatus, conditions?: any, extraFields?: any): Promise<UpdateResult>; getAgentAndAmountOngoingChats( userId: any, ): Promise<{ agentId: string; username: string; lastAssignTime: Date; lastRoutingTime: Date; queueInfo: { chats: number } }>; @@ -128,7 +129,7 @@ export interface IUsersModel extends IBaseModel<IUser> { openAgentBusinessHoursByBusinessHourIdsAndAgentId(businessHourIds: any, agentId: any): any; - addBusinessHourByAgentIds(agentIds: any, businessHourId: any): any; + addBusinessHourByAgentIds(agentIds: string[], businessHourId: string): any; removeBusinessHourByAgentIds(agentIds: any, businessHourId: any): any; @@ -142,7 +143,7 @@ export interface IUsersModel extends IBaseModel<IUser> { setLivechatStatusActiveBasedOnBusinessHours(userId: any): any; - isAgentWithinBusinessHours(agentId: any): Promise<any>; + isAgentWithinBusinessHours(agentId: string): Promise<boolean>; removeBusinessHoursFromAllUsers(): any; @@ -253,7 +254,7 @@ export interface IUsersModel extends IBaseModel<IUser> { countAgents(): Promise<number>; getNextAgent(ignoreAgentId?: string, extraQuery?: Filter<IUser>): Promise<{ agentId: string; username: string } | null>; getNextBotAgent(ignoreAgentId?: string): Promise<{ agentId: string; username: string } | null>; - setLivechatStatus(userId: string, status: UserStatus): Promise<UpdateResult>; + setLivechatStatus(userId: string, status: ILivechatAgentStatus): Promise<UpdateResult>; setLivechatData(userId: string, data?: Record<string, any>): Promise<UpdateResult>; closeOffice(): Promise<void>; openOffice(): Promise<void>; @@ -374,4 +375,5 @@ export interface IUsersModel extends IBaseModel<IUser> { countRoomMembers(roomId: string): Promise<number>; countRemote(options?: FindOptions<IUser>): Promise<number>; findOneByImportId(importId: string, options?: FindOptions<IUser>): Promise<IUser | null>; + removeAgent(_id: string): Promise<UpdateResult>; } From fa6c1258d8da80f749de7ca10bdca1a0c9eb4c35 Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Fri, 30 Jun 2023 13:59:55 -0300 Subject: [PATCH 030/149] ci: update EE license (#29684) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9c250ebcc04..8c71965aa907 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,8 +32,8 @@ jobs: rc-docker-tag-alpine: '${{ steps.docker.outputs.gh-docker-tag }}.alpine' node-version: ${{ steps.var.outputs.node-version }} # this is 100% intentional, secrets are not available for forks, so ee-tests will always fail - # to avoid this, we are using a dummy license, expiring at 2023-06-30 - enterprise-license: Z2Dg0RC3kyxjuklSE6qfqyvD2xSD+oTYcS9OesJG0523r7rSPjv59LTQqPcp5E61qQYM3MOKoW3mDrurw4h78nVbsfrF2DoJZeNjRFQfIbgwcdPwtmnqPpDvAslszHY16VzM7O7EYqAqp/9mlnRzs1iJY+W3w1r6HWBlVMb9u41bl5HBSpX6Nxw8YxL4mizwOpjxewQbPQvNTLJNAW6w0nCzF5A3CKBhD9fziadedVMLOuXBuR8kIl8zbIAfqpHmL8SvakvQAbZEjWWQshmH+C9CKA5PppkmA8Q1DNWQoVtHSiYDK8RRjAEx+0oGflklzFyhJFDvD+ohZduNtNCgrJmxP5VFrVrLSK4BXgTSwwnaSKa2N+Qx0CmuRfu7nCPc1Cf6h6+k2TXvzkE4Z0ZJnDV1khu611glAr99bHdwF+bMX3XZI66bS8KqnHEukCt5xei25iKJ2xrfmGuiAkAuKHKzBmTEmXM0pGhkfDhA9jhxG3Atoj1A5y8vdrs88voF+UuNFZ6k9sKtdvrWIWClnkatPE+41ggbzCsOhFz07BvRWaEtw2Kenipl4Vtag4qmFpUaUfsuouH99M3gDlysDZO3x5aH8yfzvFeL5WDMvsmdEHNLpHl89WsPCONvx0JjRSdwcCA1NrRuVy1Ncu0S0bRByn7HZqoY9u6HPkXKBxQ= + # to avoid this, we are using a dummy license, expiring at 2024-06-30 + enterprise-license: WMa5i+/t/LZbYOj8u3XUkivRhWBtWO6ycUjaZoVAw2DxMfdyBIAa2gMMI4x7Z2BrTZIZhFEImfOxcXcgD0QbXHGBJaMI+eYG+eofnVWi2VA7RWbpvWTULgPFgyJ4UEFeCOzVjcBLTQbmMSam3u0RlekWJkfAO0KnmLtsaEYNNA2rz1U+CLI/CdNGfdqrBu5PZZbGkH0KEzyIZMaykOjzvX+C6vd7fRxh23HecwhkBbqE8eQsCBt2ad0qC4MoVXsDaSOmSzGW+aXjuXt/9zjvrLlsmWQTSlkrEHdNkdywm0UkGxqz3+CP99n0WggUBioUiChjMuNMoceWvDvmxYP9Ml2NpYU7SnfhjmMFyXOah8ofzv8w509Y7XODvQBz+iB4Co9YnF3vT96HDDQyAV5t4jATE+0t37EAXmwjTi3qqyP7DLGK/revl+mlcwJ5kS4zZBsm1E4519FkXQOZSyWRnPdjqvh4mCLqoispZ49wKvklDvjPxCSP9us6cVXLDg7NTJr/4pfxLPOkvv7qCgugDvlDx17bXpQFPSDxmpw66FLzvb5Id0dkWjOzrRYSXb0bFWoUQjtHFzmcpFkyVhOKrQ9zA9+Zm7vXmU9Y2l2dK79EloOuHMSYAqsPEag8GMW6vI/cT4iIjHGGDePKnD0HblvTEKzql11cfT/abf2IiaY= steps: - uses: Bhacaz/checkout-files@v2 with: From 389ba42fd016531b0cd63009e226323ae5275681 Mon Sep 17 00:00:00 2001 From: rocketchat-github-ci <buildmaster@rocket.chat> Date: Fri, 30 Jun 2023 19:16:23 +0000 Subject: [PATCH 031/149] 6.2.9 --- .changeset/hip-comics-drop.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/hip-comics-drop.md diff --git a/.changeset/hip-comics-drop.md b/.changeset/hip-comics-drop.md new file mode 100644 index 000000000000..a845151cc840 --- /dev/null +++ b/.changeset/hip-comics-drop.md @@ -0,0 +1,2 @@ +--- +--- From b1d103b1d58a4b67595b7fff2404becc2145b1a3 Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:40:00 -0300 Subject: [PATCH 032/149] fix(meteor): Video Record button disabled on iOS browsers (#29649) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .changeset/itchy-hotels-care.md | 5 +++++ .../ui/client/lib/recorderjs/videoRecorder.ts | 18 +++++++++++------- .../VideoMessageRecorder.tsx | 16 ++++++++++++---- .../actions/VideoMessageAction.tsx | 3 ++- 4 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 .changeset/itchy-hotels-care.md diff --git a/.changeset/itchy-hotels-care.md b/.changeset/itchy-hotels-care.md new file mode 100644 index 000000000000..93aed3ed6dce --- /dev/null +++ b/.changeset/itchy-hotels-care.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed video message button disabled on iOS browsers diff --git a/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts b/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts index 9d6a44521894..10424c5b3f86 100644 --- a/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts +++ b/apps/meteor/app/ui/client/lib/recorderjs/videoRecorder.ts @@ -17,6 +17,16 @@ class VideoRecorder { private mediaRecorder: MediaRecorder | undefined; + public getSupportedMimeTypes() { + if (window.MediaRecorder.isTypeSupported('video/webm')) { + return 'video/webm; codecs=vp8,opus'; + } + if (window.MediaRecorder.isTypeSupported('video/mp4')) { + return 'video/mp4'; + } + return ''; + } + public start(videoel?: HTMLVideoElement, cb?: (this: this, success: boolean) => void) { this.videoel = videoel; @@ -51,7 +61,7 @@ class VideoRecorder { return; } - this.mediaRecorder = new MediaRecorder(this.stream, { mimeType: 'video/webm; codecs=vp8,opus' }); + this.mediaRecorder = new MediaRecorder(this.stream, { mimeType: this.getSupportedMimeTypes() }); this.mediaRecorder.ondataavailable = (blobev) => { this.chunks.push(blobev.data); if (!this.recordingAvailable.get()) { @@ -66,7 +76,6 @@ class VideoRecorder { if (!this.videoel) { return; } - this.stream = stream; try { @@ -76,11 +85,6 @@ class VideoRecorder { this.videoel.src = URL.createObjectURL(stream as unknown as MediaSource | Blob); } - this.videoel.muted = true; - this.videoel.onloadedmetadata = () => { - void this.videoel?.play(); - }; - this.started = true; return this.cameraStarted.set(true); } diff --git a/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx b/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx index f9c1f2ed5a97..5d43a07d8b0b 100644 --- a/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx +++ b/apps/meteor/client/views/composer/VideoMessageRecorder/VideoMessageRecorder.tsx @@ -29,6 +29,14 @@ const videoContainerClass = css` } `; +const getVideoRecordingExtension = () => { + const supported = VideoRecorder.getSupportedMimeTypes(); + if (supported.match(/video\/webm/)) { + return 'webm'; + } + return 'mp4'; +}; + const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessageRecorderProps) => { const t = useTranslation(); const videoRef = useRef<HTMLVideoElement>(null); @@ -75,8 +83,8 @@ const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessag const handleSendRecord = async () => { const cb = async (blob: Blob) => { - const fileName = `${t('Video_record')}.webm`; - const file = new File([blob], fileName, { type: 'video/webm' }); + const fileName = `${t('Video_record')}.${getVideoRecordingExtension()}`; + const file = new File([blob], fileName, { type: VideoRecorder.getSupportedMimeTypes().split(';')[0] }); await chat?.flows.uploadFiles([file]); chat?.composer?.setRecordingVideo(false); }; @@ -94,7 +102,7 @@ const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessag }; useEffect(() => { - if (!window.MediaRecorder.isTypeSupported('video/webm; codecs=vp8,opus')) { + if (!VideoRecorder.getSupportedMimeTypes()) { return dispatchToastMessage({ type: 'error', message: t('Browser_does_not_support_recording_video') }); } @@ -109,7 +117,7 @@ const VideoMessageRecorder = ({ rid, tmid, chatContext, reference }: VideoMessag <PositionAnimated visible='visible' anchor={reference} placement='top-end'> <Box bg='light' padding={4} borderRadius={4} elevation='2'> <Box className={videoContainerClass} overflow='hidden' height={240} borderRadius={4}> - <video ref={videoRef} width={320} height={240} /> + <video muted autoPlay playsInline ref={videoRef} width={320} height={240} /> </Box> <Box mbs={4} display='flex' justifyContent='space-between'> <Button small onClick={handleRecord}> diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx index ec7796830a1d..d77be675243f 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/actions/VideoMessageAction.tsx @@ -5,6 +5,7 @@ import { useTranslation, useSetting } from '@rocket.chat/ui-contexts'; import type { AllHTMLAttributes } from 'react'; import React, { useEffect, useMemo } from 'react'; +import { VideoRecorder } from '../../../../../../../../../app/ui/client/lib/recorderjs/videoRecorder'; import type { ChatAPI } from '../../../../../../../../lib/chats/ChatAPI'; import { useChat } from '../../../../../../contexts/ChatContext'; import { useMediaActionTitle } from '../../hooks/useMediaActionTitle'; @@ -33,7 +34,7 @@ const VideoMessageAction = ({ collapsed, chatContext, disabled, ...props }: Vide isVideoRecorderEnabled && !fileUploadMediaTypeBlackList?.match(/video\/webm|video\/\*/i) && (!fileUploadMediaTypeWhiteList || fileUploadMediaTypeWhiteList.match(/video\/webm|video\/\*/i)) && - window.MediaRecorder.isTypeSupported('video/webm; codecs=vp8,opus'), + Boolean(VideoRecorder.getSupportedMimeTypes()), ), [fileUploadMediaTypeBlackList, fileUploadMediaTypeWhiteList, isFileUploadEnabled, isPermissionDenied, isVideoRecorderEnabled], ); From a6115380fa49bc4cebdf749bba3644d08a89064e Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Thu, 22 Jun 2023 20:46:57 -0300 Subject: [PATCH 033/149] fix(meteor): Message composer keeps recording even though permission was denied (#29526) --- .changeset/soft-carrots-add.md | 5 +++++ apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/soft-carrots-add.md diff --git a/.changeset/soft-carrots-add.md b/.changeset/soft-carrots-add.md new file mode 100644 index 000000000000..80aba278f3a8 --- /dev/null +++ b/.changeset/soft-carrots-add.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed an error on mobile ios browser where if you started recording audio and denied permission, it would look like it is still recording diff --git a/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts b/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts index 27eb996e9da6..d8bd8cbb323c 100644 --- a/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts +++ b/apps/meteor/app/ui/client/lib/recorderjs/AudioRecorder.ts @@ -76,6 +76,7 @@ export class AudioRecorder { this.destroyStream(); this.destroyAudioContext(); cb?.call(this, false); + throw error; } } From 6ff6ba96609508d114a8a81ae0390247c44c538d Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Fri, 30 Jun 2023 16:49:37 -0300 Subject: [PATCH 034/149] chore: add change stream option to get full doc (#29627) --- .../meteor/server/database/DatabaseWatcher.ts | 19 ++- .../database/convertChangeStreamPayload.ts | 1 + .../server/database/watchCollections.ts | 58 +++++++--- .../modules/watchers/watchers.module.ts | 108 +++++++++--------- ee/apps/stream-hub-service/src/service.ts | 6 +- 5 files changed, 112 insertions(+), 80 deletions(-) diff --git a/apps/meteor/server/database/DatabaseWatcher.ts b/apps/meteor/server/database/DatabaseWatcher.ts index ff42f9721232..b0b6058b1e9d 100644 --- a/apps/meteor/server/database/DatabaseWatcher.ts +++ b/apps/meteor/server/database/DatabaseWatcher.ts @@ -8,7 +8,7 @@ import { MongoClient } from 'mongodb'; import type { Logger } from '../lib/logger/Logger'; import { convertChangeStreamPayload } from './convertChangeStreamPayload'; import { convertOplogPayload } from './convertOplogPayload'; -import { watchCollections } from './watchCollections'; +import { getWatchCollections } from './watchCollections'; const instancePing = parseInt(String(process.env.MULTIPLE_INSTANCES_PING_INTERVAL)) || 10000; @@ -28,6 +28,8 @@ const ignoreChangeStream = ['yes', 'true'].includes(String(process.env.IGNORE_CH const useMeteorOplog = ['yes', 'true'].includes(String(process.env.USE_NATIVE_OPLOG).toLowerCase()); +const useFullDocument = ['yes', 'true'].includes(String(process.env.CHANGESTREAM_FULL_DOCUMENT).toLowerCase()); + export class DatabaseWatcher extends EventEmitter { private db: Db; @@ -44,6 +46,8 @@ export class DatabaseWatcher extends EventEmitter { */ private lastDocTS: Date; + private watchCollections: string[]; + // eslint-disable-next-line @typescript-eslint/naming-convention constructor({ db, _oplogHandle, metrics, logger: LoggerClass }: { db: Db; _oplogHandle?: any; metrics?: any; logger: typeof Logger }) { super(); @@ -55,6 +59,8 @@ export class DatabaseWatcher extends EventEmitter { } async watch(): Promise<void> { + this.watchCollections = getWatchCollections(); + if (useMeteorOplog) { // TODO remove this when updating to Meteor 2.8 this.logger.warn( @@ -121,7 +127,7 @@ export class DatabaseWatcher extends EventEmitter { const stream = cursor.stream(); stream.on('data', (doc) => { - const doesMatter = watchCollections.some((collection) => doc.ns === `${dbName}.${collection}`); + const doesMatter = this.watchCollections.some((collection) => doc.ns === `${dbName}.${collection}`); if (!doesMatter) { return; } @@ -143,7 +149,7 @@ export class DatabaseWatcher extends EventEmitter { this.logger.startup('Using Meteor oplog'); - watchCollections.forEach((collection) => { + this.watchCollections.forEach((collection) => { this._oplogHandle.onOplogEntry({ collection }, (event: any) => { this.emitDoc(collection, convertOplogPayload(event)); }); @@ -152,7 +158,10 @@ export class DatabaseWatcher extends EventEmitter { private watchChangeStream(resumeToken?: unknown): void { try { - const options = resumeToken ? { startAfter: resumeToken } : {}; + const options = { + ...(useFullDocument ? { fullDocument: 'updateLookup' } : {}), + ...(resumeToken ? { startAfter: resumeToken } : {}), + }; let lastEvent: unknown; @@ -166,7 +175,7 @@ export class DatabaseWatcher extends EventEmitter { { $match: { 'operationType': { $in: ['insert', 'update', 'delete'] }, - 'ns.coll': { $in: watchCollections }, + 'ns.coll': { $in: this.watchCollections }, }, }, ], diff --git a/apps/meteor/server/database/convertChangeStreamPayload.ts b/apps/meteor/server/database/convertChangeStreamPayload.ts index 728b7a43111e..870e5abce381 100644 --- a/apps/meteor/server/database/convertChangeStreamPayload.ts +++ b/apps/meteor/server/database/convertChangeStreamPayload.ts @@ -39,6 +39,7 @@ export function convertChangeStreamPayload( action: 'update', clientAction: 'updated', id: event.documentKey._id, + data: event.fullDocument, diff, unset, }; diff --git a/apps/meteor/server/database/watchCollections.ts b/apps/meteor/server/database/watchCollections.ts index 48421e9e681c..8b667e921f1f 100644 --- a/apps/meteor/server/database/watchCollections.ts +++ b/apps/meteor/server/database/watchCollections.ts @@ -17,21 +17,43 @@ import { LivechatPriority, } from '@rocket.chat/models'; -export const watchCollections = [ - Messages.getCollectionName(), - Users.getCollectionName(), - Subscriptions.getCollectionName(), - LivechatInquiry.getCollectionName(), - LivechatDepartmentAgents.getCollectionName(), - Permissions.getCollectionName(), - Roles.getCollectionName(), - Rooms.getCollectionName(), - LoginServiceConfiguration.getCollectionName(), - InstanceStatus.getCollectionName(), - IntegrationHistory.getCollectionName(), - Integrations.getCollectionName(), - EmailInbox.getCollectionName(), - PbxEvents.getCollectionName(), - Settings.getCollectionName(), - LivechatPriority.getCollectionName(), -]; +const { DBWATCHER_EXCLUDE_COLLECTIONS = '', DBWATCHER_ONLY_COLLECTIONS = '' } = process.env; + +const excludeCollections = DBWATCHER_EXCLUDE_COLLECTIONS.split(',') + .map((collection) => collection.trim()) + .filter(Boolean); + +const onlyCollections = DBWATCHER_ONLY_COLLECTIONS.split(',') + .map((collection) => collection.trim()) + .filter(Boolean); + +export function getWatchCollections(): string[] { + const collections = [ + Messages.getCollectionName(), + Users.getCollectionName(), + Subscriptions.getCollectionName(), + LivechatInquiry.getCollectionName(), + LivechatDepartmentAgents.getCollectionName(), + Permissions.getCollectionName(), + Roles.getCollectionName(), + Rooms.getCollectionName(), + LoginServiceConfiguration.getCollectionName(), + InstanceStatus.getCollectionName(), + IntegrationHistory.getCollectionName(), + Integrations.getCollectionName(), + EmailInbox.getCollectionName(), + PbxEvents.getCollectionName(), + Settings.getCollectionName(), + LivechatPriority.getCollectionName(), + ]; + + if (onlyCollections.length > 0) { + return collections.filter((collection) => onlyCollections.includes(collection)); + } + + if (excludeCollections.length > 0) { + return collections.filter((collection) => !excludeCollections.includes(collection)); + } + + return collections; +} diff --git a/apps/meteor/server/modules/watchers/watchers.module.ts b/apps/meteor/server/modules/watchers/watchers.module.ts index 4ce7432bf124..c039259d7ddf 100644 --- a/apps/meteor/server/modules/watchers/watchers.module.ts +++ b/apps/meteor/server/modules/watchers/watchers.module.ts @@ -120,59 +120,61 @@ export function initWatchers(watcher: DatabaseWatcher, broadcast: BroadcastCallb } // Override data cuz we do not publish all fields - const subscription = await Subscriptions.findOneById< - Pick< - ISubscription, - | 't' - | 'ts' - | 'ls' - | 'lr' - | 'name' - | 'fname' - | 'rid' - | 'code' - | 'f' - | 'u' - | 'open' - | 'alert' - | 'roles' - | 'unread' - | 'prid' - | 'userMentions' - | 'groupMentions' - | 'archived' - | 'audioNotificationValue' - | 'desktopNotifications' - | 'mobilePushNotifications' - | 'emailNotifications' - | 'desktopPrefOrigin' - | 'mobilePrefOrigin' - | 'emailPrefOrigin' - | 'unreadAlert' - | '_updatedAt' - | 'blocked' - | 'blocker' - | 'autoTranslate' - | 'autoTranslateLanguage' - | 'disableNotifications' - | 'hideUnreadStatus' - | 'hideMentionStatus' - | 'muteGroupMentions' - | 'ignored' - | 'E2EKey' - | 'E2ESuggestedKey' - | 'tunread' - | 'tunreadGroup' - | 'tunreadUser' - - // Omnichannel fields - | 'department' - | 'v' - | 'onHold' - > - >(id, { - projection: subscriptionFields, - }); + const subscription = + data || + (await Subscriptions.findOneById< + Pick< + ISubscription, + | 't' + | 'ts' + | 'ls' + | 'lr' + | 'name' + | 'fname' + | 'rid' + | 'code' + | 'f' + | 'u' + | 'open' + | 'alert' + | 'roles' + | 'unread' + | 'prid' + | 'userMentions' + | 'groupMentions' + | 'archived' + | 'audioNotificationValue' + | 'desktopNotifications' + | 'mobilePushNotifications' + | 'emailNotifications' + | 'desktopPrefOrigin' + | 'mobilePrefOrigin' + | 'emailPrefOrigin' + | 'unreadAlert' + | '_updatedAt' + | 'blocked' + | 'blocker' + | 'autoTranslate' + | 'autoTranslateLanguage' + | 'disableNotifications' + | 'hideUnreadStatus' + | 'hideMentionStatus' + | 'muteGroupMentions' + | 'ignored' + | 'E2EKey' + | 'E2ESuggestedKey' + | 'tunread' + | 'tunreadGroup' + | 'tunreadUser' + + // Omnichannel fields + | 'department' + | 'v' + | 'onHold' + > + >(id, { + projection: subscriptionFields, + })); if (!subscription) { return; diff --git a/ee/apps/stream-hub-service/src/service.ts b/ee/apps/stream-hub-service/src/service.ts index 81175a3e216a..adfa40fd4cb6 100755 --- a/ee/apps/stream-hub-service/src/service.ts +++ b/ee/apps/stream-hub-service/src/service.ts @@ -3,9 +3,11 @@ import polka from 'polka'; import { api } from '@rocket.chat/core-services'; import { broker } from '../../../../apps/meteor/ee/server/startup/broker'; +import { DatabaseWatcher } from '../../../../apps/meteor/server/database/DatabaseWatcher'; import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; import { Logger } from '../../../../apps/meteor/server/lib/logger/Logger'; +import { StreamHub } from './StreamHub'; const PORT = process.env.PORT || 3035; @@ -18,10 +20,6 @@ const PORT = process.env.PORT || 3035; api.setBroker(broker); - // need to import service after models are registered - const { StreamHub } = await import('./StreamHub'); - const { DatabaseWatcher } = await import('../../../../apps/meteor/server/database/DatabaseWatcher'); - // TODO having to import Logger to pass as a param is a temporary solution. logger should come from the service (either from broker or api) const watcher = new DatabaseWatcher({ db, logger: Logger }); From b88f5561f69faa17bcc9dc409bd3169f04d6d0cc Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Fri, 30 Jun 2023 16:49:37 -0300 Subject: [PATCH 035/149] chore: add change stream option to get full doc (#29627) --- .../meteor/server/database/DatabaseWatcher.ts | 19 ++++-- .../database/convertChangeStreamPayload.ts | 1 + .../server/database/watchCollections.ts | 58 +++++++++++++------ .../modules/watchers/watchers.module.ts | 8 ++- ee/apps/stream-hub-service/src/service.ts | 6 +- 5 files changed, 62 insertions(+), 30 deletions(-) diff --git a/apps/meteor/server/database/DatabaseWatcher.ts b/apps/meteor/server/database/DatabaseWatcher.ts index ff42f9721232..b0b6058b1e9d 100644 --- a/apps/meteor/server/database/DatabaseWatcher.ts +++ b/apps/meteor/server/database/DatabaseWatcher.ts @@ -8,7 +8,7 @@ import { MongoClient } from 'mongodb'; import type { Logger } from '../lib/logger/Logger'; import { convertChangeStreamPayload } from './convertChangeStreamPayload'; import { convertOplogPayload } from './convertOplogPayload'; -import { watchCollections } from './watchCollections'; +import { getWatchCollections } from './watchCollections'; const instancePing = parseInt(String(process.env.MULTIPLE_INSTANCES_PING_INTERVAL)) || 10000; @@ -28,6 +28,8 @@ const ignoreChangeStream = ['yes', 'true'].includes(String(process.env.IGNORE_CH const useMeteorOplog = ['yes', 'true'].includes(String(process.env.USE_NATIVE_OPLOG).toLowerCase()); +const useFullDocument = ['yes', 'true'].includes(String(process.env.CHANGESTREAM_FULL_DOCUMENT).toLowerCase()); + export class DatabaseWatcher extends EventEmitter { private db: Db; @@ -44,6 +46,8 @@ export class DatabaseWatcher extends EventEmitter { */ private lastDocTS: Date; + private watchCollections: string[]; + // eslint-disable-next-line @typescript-eslint/naming-convention constructor({ db, _oplogHandle, metrics, logger: LoggerClass }: { db: Db; _oplogHandle?: any; metrics?: any; logger: typeof Logger }) { super(); @@ -55,6 +59,8 @@ export class DatabaseWatcher extends EventEmitter { } async watch(): Promise<void> { + this.watchCollections = getWatchCollections(); + if (useMeteorOplog) { // TODO remove this when updating to Meteor 2.8 this.logger.warn( @@ -121,7 +127,7 @@ export class DatabaseWatcher extends EventEmitter { const stream = cursor.stream(); stream.on('data', (doc) => { - const doesMatter = watchCollections.some((collection) => doc.ns === `${dbName}.${collection}`); + const doesMatter = this.watchCollections.some((collection) => doc.ns === `${dbName}.${collection}`); if (!doesMatter) { return; } @@ -143,7 +149,7 @@ export class DatabaseWatcher extends EventEmitter { this.logger.startup('Using Meteor oplog'); - watchCollections.forEach((collection) => { + this.watchCollections.forEach((collection) => { this._oplogHandle.onOplogEntry({ collection }, (event: any) => { this.emitDoc(collection, convertOplogPayload(event)); }); @@ -152,7 +158,10 @@ export class DatabaseWatcher extends EventEmitter { private watchChangeStream(resumeToken?: unknown): void { try { - const options = resumeToken ? { startAfter: resumeToken } : {}; + const options = { + ...(useFullDocument ? { fullDocument: 'updateLookup' } : {}), + ...(resumeToken ? { startAfter: resumeToken } : {}), + }; let lastEvent: unknown; @@ -166,7 +175,7 @@ export class DatabaseWatcher extends EventEmitter { { $match: { 'operationType': { $in: ['insert', 'update', 'delete'] }, - 'ns.coll': { $in: watchCollections }, + 'ns.coll': { $in: this.watchCollections }, }, }, ], diff --git a/apps/meteor/server/database/convertChangeStreamPayload.ts b/apps/meteor/server/database/convertChangeStreamPayload.ts index 728b7a43111e..870e5abce381 100644 --- a/apps/meteor/server/database/convertChangeStreamPayload.ts +++ b/apps/meteor/server/database/convertChangeStreamPayload.ts @@ -39,6 +39,7 @@ export function convertChangeStreamPayload( action: 'update', clientAction: 'updated', id: event.documentKey._id, + data: event.fullDocument, diff, unset, }; diff --git a/apps/meteor/server/database/watchCollections.ts b/apps/meteor/server/database/watchCollections.ts index 48421e9e681c..8b667e921f1f 100644 --- a/apps/meteor/server/database/watchCollections.ts +++ b/apps/meteor/server/database/watchCollections.ts @@ -17,21 +17,43 @@ import { LivechatPriority, } from '@rocket.chat/models'; -export const watchCollections = [ - Messages.getCollectionName(), - Users.getCollectionName(), - Subscriptions.getCollectionName(), - LivechatInquiry.getCollectionName(), - LivechatDepartmentAgents.getCollectionName(), - Permissions.getCollectionName(), - Roles.getCollectionName(), - Rooms.getCollectionName(), - LoginServiceConfiguration.getCollectionName(), - InstanceStatus.getCollectionName(), - IntegrationHistory.getCollectionName(), - Integrations.getCollectionName(), - EmailInbox.getCollectionName(), - PbxEvents.getCollectionName(), - Settings.getCollectionName(), - LivechatPriority.getCollectionName(), -]; +const { DBWATCHER_EXCLUDE_COLLECTIONS = '', DBWATCHER_ONLY_COLLECTIONS = '' } = process.env; + +const excludeCollections = DBWATCHER_EXCLUDE_COLLECTIONS.split(',') + .map((collection) => collection.trim()) + .filter(Boolean); + +const onlyCollections = DBWATCHER_ONLY_COLLECTIONS.split(',') + .map((collection) => collection.trim()) + .filter(Boolean); + +export function getWatchCollections(): string[] { + const collections = [ + Messages.getCollectionName(), + Users.getCollectionName(), + Subscriptions.getCollectionName(), + LivechatInquiry.getCollectionName(), + LivechatDepartmentAgents.getCollectionName(), + Permissions.getCollectionName(), + Roles.getCollectionName(), + Rooms.getCollectionName(), + LoginServiceConfiguration.getCollectionName(), + InstanceStatus.getCollectionName(), + IntegrationHistory.getCollectionName(), + Integrations.getCollectionName(), + EmailInbox.getCollectionName(), + PbxEvents.getCollectionName(), + Settings.getCollectionName(), + LivechatPriority.getCollectionName(), + ]; + + if (onlyCollections.length > 0) { + return collections.filter((collection) => onlyCollections.includes(collection)); + } + + if (excludeCollections.length > 0) { + return collections.filter((collection) => !excludeCollections.includes(collection)); + } + + return collections; +} diff --git a/apps/meteor/server/modules/watchers/watchers.module.ts b/apps/meteor/server/modules/watchers/watchers.module.ts index 0722d93f0b2c..ee988d308ed1 100644 --- a/apps/meteor/server/modules/watchers/watchers.module.ts +++ b/apps/meteor/server/modules/watchers/watchers.module.ts @@ -120,9 +120,11 @@ export function initWatchers(watcher: DatabaseWatcher, broadcast: BroadcastCallb } // Override data cuz we do not publish all fields - const subscription = await Subscriptions.findOneById<Pick<ISubscription, keyof typeof subscriptionFields>>(id, { - projection: subscriptionFields, - }); + const subscription = + data || + (await Subscriptions.findOneById<Pick<ISubscription, keyof typeof subscriptionFields>>(id, { + projection: subscriptionFields, + })); if (!subscription) { return; } diff --git a/ee/apps/stream-hub-service/src/service.ts b/ee/apps/stream-hub-service/src/service.ts index 81175a3e216a..adfa40fd4cb6 100755 --- a/ee/apps/stream-hub-service/src/service.ts +++ b/ee/apps/stream-hub-service/src/service.ts @@ -3,9 +3,11 @@ import polka from 'polka'; import { api } from '@rocket.chat/core-services'; import { broker } from '../../../../apps/meteor/ee/server/startup/broker'; +import { DatabaseWatcher } from '../../../../apps/meteor/server/database/DatabaseWatcher'; import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; import { Logger } from '../../../../apps/meteor/server/lib/logger/Logger'; +import { StreamHub } from './StreamHub'; const PORT = process.env.PORT || 3035; @@ -18,10 +20,6 @@ const PORT = process.env.PORT || 3035; api.setBroker(broker); - // need to import service after models are registered - const { StreamHub } = await import('./StreamHub'); - const { DatabaseWatcher } = await import('../../../../apps/meteor/server/database/DatabaseWatcher'); - // TODO having to import Logger to pass as a param is a temporary solution. logger should come from the service (either from broker or api) const watcher = new DatabaseWatcher({ db, logger: Logger }); From c61ebdf3b283295021559baf861c40015a8a966b Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Thu, 29 Jun 2023 11:12:19 -0600 Subject: [PATCH 036/149] ci: Add missing `context` property on compose file (#29676) --- docker-compose-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index b55d1ba3d351..cf30d21384c2 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -29,6 +29,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/authorization-service/Dockerfile + context: . args: SERVICE: authorization-service image: ghcr.io/${LOWERCASE_REPOSITORY}/authorization-service:${DOCKER_TAG} @@ -45,6 +46,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/account-service/Dockerfile + context: . args: SERVICE: account-service image: ghcr.io/${LOWERCASE_REPOSITORY}/account-service:${DOCKER_TAG} @@ -61,6 +63,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/presence-service/Dockerfile + context: . args: SERVICE: presence-service image: ghcr.io/${LOWERCASE_REPOSITORY}/presence-service:${DOCKER_TAG} @@ -77,6 +80,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/ddp-streamer/Dockerfile + context: . args: SERVICE: ddp-streamer image: ghcr.io/${LOWERCASE_REPOSITORY}/ddp-streamer-service:${DOCKER_TAG} @@ -99,6 +103,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/stream-hub-service/Dockerfile + context: . args: SERVICE: stream-hub-service image: ghcr.io/${LOWERCASE_REPOSITORY}/stream-hub-service:${DOCKER_TAG} @@ -115,6 +120,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/queue-worker/Dockerfile + context: . args: SERVICE: queue-worker image: ghcr.io/${LOWERCASE_REPOSITORY}/queue-worker-service:${DOCKER_TAG} @@ -131,6 +137,7 @@ services: platform: linux/amd64 build: dockerfile: ee/apps/omnichannel-transcript/Dockerfile + context: . args: SERVICE: omnichannel-transcript image: ghcr.io/${LOWERCASE_REPOSITORY}/omnichannel-transcript-service:${DOCKER_TAG} From 93991164d89a4a24052da6cc1077ed2432f05128 Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Fri, 30 Jun 2023 13:59:55 -0300 Subject: [PATCH 037/149] ci: update EE license (#29684) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4038c09f09b8..aa344ea05c80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,8 +32,8 @@ jobs: rc-docker-tag-alpine: '${{ steps.docker.outputs.gh-docker-tag }}.alpine' node-version: ${{ steps.var.outputs.node-version }} # this is 100% intentional, secrets are not available for forks, so ee-tests will always fail - # to avoid this, we are using a dummy license, expiring at 2023-06-30 - enterprise-license: Z2Dg0RC3kyxjuklSE6qfqyvD2xSD+oTYcS9OesJG0523r7rSPjv59LTQqPcp5E61qQYM3MOKoW3mDrurw4h78nVbsfrF2DoJZeNjRFQfIbgwcdPwtmnqPpDvAslszHY16VzM7O7EYqAqp/9mlnRzs1iJY+W3w1r6HWBlVMb9u41bl5HBSpX6Nxw8YxL4mizwOpjxewQbPQvNTLJNAW6w0nCzF5A3CKBhD9fziadedVMLOuXBuR8kIl8zbIAfqpHmL8SvakvQAbZEjWWQshmH+C9CKA5PppkmA8Q1DNWQoVtHSiYDK8RRjAEx+0oGflklzFyhJFDvD+ohZduNtNCgrJmxP5VFrVrLSK4BXgTSwwnaSKa2N+Qx0CmuRfu7nCPc1Cf6h6+k2TXvzkE4Z0ZJnDV1khu611glAr99bHdwF+bMX3XZI66bS8KqnHEukCt5xei25iKJ2xrfmGuiAkAuKHKzBmTEmXM0pGhkfDhA9jhxG3Atoj1A5y8vdrs88voF+UuNFZ6k9sKtdvrWIWClnkatPE+41ggbzCsOhFz07BvRWaEtw2Kenipl4Vtag4qmFpUaUfsuouH99M3gDlysDZO3x5aH8yfzvFeL5WDMvsmdEHNLpHl89WsPCONvx0JjRSdwcCA1NrRuVy1Ncu0S0bRByn7HZqoY9u6HPkXKBxQ= + # to avoid this, we are using a dummy license, expiring at 2024-06-30 + enterprise-license: WMa5i+/t/LZbYOj8u3XUkivRhWBtWO6ycUjaZoVAw2DxMfdyBIAa2gMMI4x7Z2BrTZIZhFEImfOxcXcgD0QbXHGBJaMI+eYG+eofnVWi2VA7RWbpvWTULgPFgyJ4UEFeCOzVjcBLTQbmMSam3u0RlekWJkfAO0KnmLtsaEYNNA2rz1U+CLI/CdNGfdqrBu5PZZbGkH0KEzyIZMaykOjzvX+C6vd7fRxh23HecwhkBbqE8eQsCBt2ad0qC4MoVXsDaSOmSzGW+aXjuXt/9zjvrLlsmWQTSlkrEHdNkdywm0UkGxqz3+CP99n0WggUBioUiChjMuNMoceWvDvmxYP9Ml2NpYU7SnfhjmMFyXOah8ofzv8w509Y7XODvQBz+iB4Co9YnF3vT96HDDQyAV5t4jATE+0t37EAXmwjTi3qqyP7DLGK/revl+mlcwJ5kS4zZBsm1E4519FkXQOZSyWRnPdjqvh4mCLqoispZ49wKvklDvjPxCSP9us6cVXLDg7NTJr/4pfxLPOkvv7qCgugDvlDx17bXpQFPSDxmpw66FLzvb5Id0dkWjOzrRYSXb0bFWoUQjtHFzmcpFkyVhOKrQ9zA9+Zm7vXmU9Y2l2dK79EloOuHMSYAqsPEag8GMW6vI/cT4iIjHGGDePKnD0HblvTEKzql11cfT/abf2IiaY= steps: - uses: Bhacaz/checkout-files@v2 with: From 8a268255f01d758094077ca17e48e6fa8119e26f Mon Sep 17 00:00:00 2001 From: rocketchat-github-ci <buildmaster@rocket.chat> Date: Fri, 30 Jun 2023 22:30:12 +0000 Subject: [PATCH 038/149] Release 6.2.9 --- .changeset/hip-comics-drop.md | 2 -- .changeset/itchy-hotels-care.md | 5 ---- .changeset/soft-carrots-add.md | 5 ---- apps/meteor/.docker/Dockerfile.rhel | 2 +- apps/meteor/CHANGELOG.md | 27 +++++++++++++++++++ apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/ee/server/services/CHANGELOG.md | 11 ++++++++ apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/CHANGELOG.md | 11 ++++++++ ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/CHANGELOG.md | 11 ++++++++ ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/CHANGELOG.md | 12 +++++++++ ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/CHANGELOG.md | 12 +++++++++ ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/CHANGELOG.md | 11 ++++++++ ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/CHANGELOG.md | 11 ++++++++ ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/CHANGELOG.md | 10 +++++++ ee/apps/stream-hub-service/package.json | 2 +- ee/packages/omnichannel-services/CHANGELOG.md | 12 +++++++++ ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/CHANGELOG.md | 7 +++++ ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/CHANGELOG.md | 9 +++++++ ee/packages/presence/package.json | 2 +- package.json | 2 +- packages/api-client/CHANGELOG.md | 8 ++++++ packages/api-client/package.json | 2 +- packages/core-services/CHANGELOG.md | 9 +++++++ packages/core-services/package.json | 2 +- packages/core-typings/CHANGELOG.md | 2 ++ packages/core-typings/package.json | 2 +- packages/instance-status/CHANGELOG.md | 7 +++++ packages/instance-status/package.json | 2 +- packages/model-typings/CHANGELOG.md | 7 +++++ packages/model-typings/package.json | 2 +- packages/models/CHANGELOG.md | 7 +++++ packages/models/package.json | 2 +- packages/rest-typings/CHANGELOG.md | 7 +++++ packages/rest-typings/package.json | 2 +- packages/ui-contexts/CHANGELOG.md | 8 ++++++ packages/ui-contexts/package.json | 2 +- 46 files changed, 222 insertions(+), 35 deletions(-) delete mode 100644 .changeset/hip-comics-drop.md delete mode 100644 .changeset/itchy-hotels-care.md delete mode 100644 .changeset/soft-carrots-add.md diff --git a/.changeset/hip-comics-drop.md b/.changeset/hip-comics-drop.md deleted file mode 100644 index a845151cc840..000000000000 --- a/.changeset/hip-comics-drop.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/itchy-hotels-care.md b/.changeset/itchy-hotels-care.md deleted file mode 100644 index 93aed3ed6dce..000000000000 --- a/.changeset/itchy-hotels-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fixed video message button disabled on iOS browsers diff --git a/.changeset/soft-carrots-add.md b/.changeset/soft-carrots-add.md deleted file mode 100644 index 80aba278f3a8..000000000000 --- a/.changeset/soft-carrots-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fixed an error on mobile ios browser where if you started recording audio and denied permission, it would look like it is still recording diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index e3746d22c0b0..583b013d274b 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 6.2.8 +ENV RC_VERSION 6.2.9 MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index c27531b45111..011b7562f890 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,5 +1,32 @@ # @rocket.chat/meteor +## 6.2.9 + +### Patch Changes + +- [#29686](https://github.com/RocketChat/Rocket.Chat/pull/29686) [`b1d103b1d5`](https://github.com/RocketChat/Rocket.Chat/commit/b1d103b1d58a4b67595b7fff2404becc2145b1a3) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - fixed video message button disabled on iOS browsers + +- [#29686](https://github.com/RocketChat/Rocket.Chat/pull/29686) [`a6115380fa`](https://github.com/RocketChat/Rocket.Chat/commit/a6115380fa49bc4cebdf749bba3644d08a89064e) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - fixed an error on mobile ios browser where if you started recording audio and denied permission, it would look like it is still recording + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/omnichannel-services@0.0.4 + - @rocket.chat/pdf-worker@0.0.4 + - @rocket.chat/presence@0.0.4 + - @rocket.chat/api-client@0.0.4 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/gazzodown@0.0.1 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/ui-contexts@0.0.4 + - @rocket.chat/models@0.0.4 + - @rocket.chat/ui-theming@0.0.1 + - @rocket.chat/fuselage-ui-kit@0.31.16 + - @rocket.chat/ui-client@0.0.1 + - @rocket.chat/ui-video-conf@0.0.1 + - @rocket.chat/web-ui-registration@0.0.1 + - @rocket.chat/instance-status@0.0.4 + ## 6.2.7 ### Patch Changes diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index ab44536c9ebe..2f6cc0c285a4 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.2.8" + "version": "6.2.9" } diff --git a/apps/meteor/ee/server/services/CHANGELOG.md b/apps/meteor/ee/server/services/CHANGELOG.md index e0e9d1ca5c11..a89b8f622d4b 100644 --- a/apps/meteor/ee/server/services/CHANGELOG.md +++ b/apps/meteor/ee/server/services/CHANGELOG.md @@ -1,5 +1,16 @@ # rocketchat-services +## 1.0.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 1.0.2 ### Patch Changes diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index 72ac960e626a..8b0123178d4c 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -1,7 +1,7 @@ { "name": "rocketchat-services", "private": true, - "version": "1.0.2", + "version": "1.0.3", "description": "Rocket.Chat Authorization service", "main": "index.js", "scripts": { diff --git a/apps/meteor/package.json b/apps/meteor/package.json index b756e6f7d981..d64375f7e5d8 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/meteor", "description": "The Ultimate Open Source WebChat Platform", - "version": "6.2.8", + "version": "6.2.9", "private": true, "author": { "name": "Rocket.Chat", diff --git a/ee/apps/account-service/CHANGELOG.md b/ee/apps/account-service/CHANGELOG.md index 1fb9d208707d..d978f4e9db2f 100644 --- a/ee/apps/account-service/CHANGELOG.md +++ b/ee/apps/account-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/account-service +## 0.1.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index 29f091a2115d..5e90de6201b9 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/account-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Account service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/authorization-service/CHANGELOG.md b/ee/apps/authorization-service/CHANGELOG.md index 815e805c9cb3..e586e5e8d60e 100644 --- a/ee/apps/authorization-service/CHANGELOG.md +++ b/ee/apps/authorization-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/authorization-service +## 0.1.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index d897508cfe38..ae4e12d76d39 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/authorization-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Authorization service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/ddp-streamer/CHANGELOG.md b/ee/apps/ddp-streamer/CHANGELOG.md index 7b94c4927772..29187bf9f2b2 100644 --- a/ee/apps/ddp-streamer/CHANGELOG.md +++ b/ee/apps/ddp-streamer/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/ddp-streamer +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + - @rocket.chat/instance-status@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index f10c8ea3b757..9caa8ceabace 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/ddp-streamer", "private": true, - "version": "0.0.3", + "version": "0.0.4", "description": "Rocket.Chat DDP-Streamer service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/omnichannel-transcript/CHANGELOG.md b/ee/apps/omnichannel-transcript/CHANGELOG.md index 9d9a9acc118b..f855a14d51b0 100644 --- a/ee/apps/omnichannel-transcript/CHANGELOG.md +++ b/ee/apps/omnichannel-transcript/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/omnichannel-transcript +## 0.1.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/omnichannel-services@0.0.4 + - @rocket.chat/pdf-worker@0.0.4 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index d99b89d16e42..3503f8b5b6f7 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/omnichannel-transcript", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/presence-service/CHANGELOG.md b/ee/apps/presence-service/CHANGELOG.md index 8891187ff08f..d0f5b777a517 100644 --- a/ee/apps/presence-service/CHANGELOG.md +++ b/ee/apps/presence-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/presence-service +## 0.1.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/presence@0.0.4 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index 9cccd3703f3e..ec56bc6c49c2 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/presence-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Presence service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/queue-worker/CHANGELOG.md b/ee/apps/queue-worker/CHANGELOG.md index 08b199f14d9c..53ef8de2e587 100644 --- a/ee/apps/queue-worker/CHANGELOG.md +++ b/ee/apps/queue-worker/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/queue-worker +## 0.1.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/omnichannel-services@0.0.4 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index efdfae6cd15b..7503f17157c0 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/queue-worker", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/stream-hub-service/CHANGELOG.md b/ee/apps/stream-hub-service/CHANGELOG.md index de639f94831a..377e849b5cf8 100644 --- a/ee/apps/stream-hub-service/CHANGELOG.md +++ b/ee/apps/stream-hub-service/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/stream-hub-service +## 0.1.3 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index 3d481a9160b7..cc1a1686a940 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/stream-hub-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Stream Hub service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/packages/omnichannel-services/CHANGELOG.md b/ee/packages/omnichannel-services/CHANGELOG.md index 4b169eff955c..291ac808eaa8 100644 --- a/ee/packages/omnichannel-services/CHANGELOG.md +++ b/ee/packages/omnichannel-services/CHANGELOG.md @@ -1,5 +1,17 @@ # @rocket.chat/omnichannel-services +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/pdf-worker@0.0.4 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index 3eefbc9cd26a..8e88592b2d1e 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/omnichannel-services", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/ee/packages/pdf-worker/CHANGELOG.md b/ee/packages/pdf-worker/CHANGELOG.md index bf44622d6166..1953495a9e43 100644 --- a/ee/packages/pdf-worker/CHANGELOG.md +++ b/ee/packages/pdf-worker/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/pdf-worker +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index fab3db3504c0..be365fb5cb05 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/pdf-worker", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@storybook/addon-actions": "~6.5.14", diff --git a/ee/packages/presence/CHANGELOG.md b/ee/packages/presence/CHANGELOG.md index bc882fd8ec0f..74e689dcefdf 100644 --- a/ee/packages/presence/CHANGELOG.md +++ b/ee/packages/presence/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/presence +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 6894a268f110..9112b3daa68c 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/presence", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@babel/core": "^7.20.5", diff --git a/package.json b/package.json index 5e6cc689277d..f12f83ec5e71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.2.8", + "version": "6.2.9", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/api-client/CHANGELOG.md b/packages/api-client/CHANGELOG.md index 6e3b931cfdab..88b2916f94f0 100644 --- a/packages/api-client/CHANGELOG.md +++ b/packages/api-client/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/api-client +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/packages/api-client/package.json b/packages/api-client/package.json index 1fad6dbde4e0..168cc248689a 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/api-client", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/core-services/CHANGELOG.md b/packages/core-services/CHANGELOG.md index f9f550c86fb5..8434f1102487 100644 --- a/packages/core-services/CHANGELOG.md +++ b/packages/core-services/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/core-services +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/packages/core-services/package.json b/packages/core-services/package.json index a608d182e65d..44417b19e782 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-services", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/core-typings/CHANGELOG.md b/packages/core-typings/CHANGELOG.md index 11526ef6d5a9..217cf6e00d2d 100644 --- a/packages/core-typings/CHANGELOG.md +++ b/packages/core-typings/CHANGELOG.md @@ -1,5 +1,7 @@ # @rocket.chat/core-typings +## 6.2.9 + ## 6.2.7 ## 6.2.6 diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index ce13876bb9f7..6895e7ac55ca 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-typings", - "version": "6.2.8", + "version": "6.2.9", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/instance-status/CHANGELOG.md b/packages/instance-status/CHANGELOG.md index e8726035358d..243f25c30227 100644 --- a/packages/instance-status/CHANGELOG.md +++ b/packages/instance-status/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/instance-status +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index 1a9cbeb3967b..7002956ed343 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/instance-status", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/model-typings/CHANGELOG.md b/packages/model-typings/CHANGELOG.md index 71417ee3e9ef..4f0e3b55aa22 100644 --- a/packages/model-typings/CHANGELOG.md +++ b/packages/model-typings/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/model-typings +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 312410396b9c..690ffe0c21c1 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/model-typings", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index 87ccf5f8c057..55191c41a078 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/models +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/model-typings@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/packages/models/package.json b/packages/models/package.json index 99909f53ec30..d9ed6794d8ba 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/models", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/rest-typings/CHANGELOG.md b/packages/rest-typings/CHANGELOG.md index 39debf6d6189..3b192d977dc4 100644 --- a/packages/rest-typings/CHANGELOG.md +++ b/packages/rest-typings/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/rest-typings +## 6.2.9 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + ## 6.2.7 ### Patch Changes diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 63f5bca2464b..57bcd3112dc3 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.2.8", + "version": "6.2.9", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/ui-contexts/CHANGELOG.md b/packages/ui-contexts/CHANGELOG.md index 465c0fad5029..fef4ff588905 100644 --- a/packages/ui-contexts/CHANGELOG.md +++ b/packages/ui-contexts/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/ui-contexts +## 0.0.4 + +### Patch Changes + +- Updated dependencies []: + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 123a092e2958..31793d37f3c0 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-contexts", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/core-typings": "workspace:^", From ee75fc2e82784f92831e5d841cc1a1477a6d79c2 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Fri, 30 Jun 2023 22:41:23 -0300 Subject: [PATCH 039/149] chore: bump turbo (#29696) --- package.json | 2 +- turbo.json | 4 ++ yarn.lock | 130 ++++++++++++--------------------------------------- 3 files changed, 35 insertions(+), 101 deletions(-) diff --git a/package.json b/package.json index 97f4e1780418..7a79038a3acf 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@types/chart.js": "^2.9.37", "@types/js-yaml": "^4.0.5", "husky": "^7.0.4", - "turbo": "~1.2.16" + "turbo": "~1.10.7" }, "workspaces": [ "apps/*", diff --git a/turbo.json b/turbo.json index 514ed788ab5d..e617f46d0bea 100644 --- a/turbo.json +++ b/turbo.json @@ -39,6 +39,10 @@ "dependsOn": ["^build"], "cache": false }, + "@rocket.chat/meteor#build": { + "dependsOn": ["^build"], + "cache": false + }, "@rocket.chat/i18n#build": { "dependsOn": ["^build"], "cache": false diff --git a/yarn.lock b/yarn.lock index 89e8ef56d15b..7c98d73a7961 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35948,7 +35948,7 @@ __metadata: "@types/chart.js": ^2.9.37 "@types/js-yaml": ^4.0.5 husky: ^7.0.4 - turbo: ~1.2.16 + turbo: ~1.10.7 languageName: unknown linkType: soft @@ -39339,144 +39339,74 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-darwin-64@npm:1.2.16" +"turbo-darwin-64@npm:1.10.7": + version: 1.10.7 + resolution: "turbo-darwin-64@npm:1.10.7" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-darwin-arm64@npm:1.2.16" +"turbo-darwin-arm64@npm:1.10.7": + version: 1.10.7 + resolution: "turbo-darwin-arm64@npm:1.10.7" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-freebsd-64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-freebsd-64@npm:1.2.16" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"turbo-freebsd-arm64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-freebsd-arm64@npm:1.2.16" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - -"turbo-linux-32@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-linux-32@npm:1.2.16" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - -"turbo-linux-64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-linux-64@npm:1.2.16" +"turbo-linux-64@npm:1.10.7": + version: 1.10.7 + resolution: "turbo-linux-64@npm:1.10.7" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-linux-arm64@npm:1.2.16" +"turbo-linux-arm64@npm:1.10.7": + version: 1.10.7 + resolution: "turbo-linux-arm64@npm:1.10.7" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-linux-arm@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-linux-arm@npm:1.2.16" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"turbo-linux-mips64le@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-linux-mips64le@npm:1.2.16" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - -"turbo-linux-ppc64le@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-linux-ppc64le@npm:1.2.16" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - -"turbo-windows-32@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-windows-32@npm:1.2.16" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"turbo-windows-64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-windows-64@npm:1.2.16" +"turbo-windows-64@npm:1.10.7": + version: 1.10.7 + resolution: "turbo-windows-64@npm:1.10.7" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.2.16": - version: 1.2.16 - resolution: "turbo-windows-arm64@npm:1.2.16" +"turbo-windows-arm64@npm:1.10.7": + version: 1.10.7 + resolution: "turbo-windows-arm64@npm:1.10.7" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"turbo@npm:~1.2.16": - version: 1.2.16 - resolution: "turbo@npm:1.2.16" - dependencies: - turbo-darwin-64: 1.2.16 - turbo-darwin-arm64: 1.2.16 - turbo-freebsd-64: 1.2.16 - turbo-freebsd-arm64: 1.2.16 - turbo-linux-32: 1.2.16 - turbo-linux-64: 1.2.16 - turbo-linux-arm: 1.2.16 - turbo-linux-arm64: 1.2.16 - turbo-linux-mips64le: 1.2.16 - turbo-linux-ppc64le: 1.2.16 - turbo-windows-32: 1.2.16 - turbo-windows-64: 1.2.16 - turbo-windows-arm64: 1.2.16 +"turbo@npm:~1.10.7": + version: 1.10.7 + resolution: "turbo@npm:1.10.7" + dependencies: + turbo-darwin-64: 1.10.7 + turbo-darwin-arm64: 1.10.7 + turbo-linux-64: 1.10.7 + turbo-linux-arm64: 1.10.7 + turbo-windows-64: 1.10.7 + turbo-windows-arm64: 1.10.7 dependenciesMeta: turbo-darwin-64: optional: true turbo-darwin-arm64: optional: true - turbo-freebsd-64: - optional: true - turbo-freebsd-arm64: - optional: true - turbo-linux-32: - optional: true turbo-linux-64: optional: true - turbo-linux-arm: - optional: true turbo-linux-arm64: optional: true - turbo-linux-mips64le: - optional: true - turbo-linux-ppc64le: - optional: true - turbo-windows-32: - optional: true turbo-windows-64: optional: true turbo-windows-arm64: optional: true bin: turbo: bin/turbo - checksum: 245b1b28af153edd14ab35a4812a8651b1e7cbae44ce3f15b53e0a73a3cc9c4ea39734f18c966403496c37876dd57053070b5b7eb736a14f1bc7e1bfd36566f3 + checksum: 58329caf13b5fef284ccfc21bd1f841023122371d75d2202be809a385aade84fd886cf5c2093323c115c497d503c9c34e1f7ae09e13d06d1dcb4b649883f60dd languageName: node linkType: hard From baaa38f7f43dcbb47646d1fb3a74aef1d7115b67 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Sat, 1 Jul 2023 17:37:37 -0300 Subject: [PATCH 040/149] fix: Required custom field consider blank space as valid input (#29635) --- .changeset/olive-pears-sell.md | 5 ++ .../src/components/CustomFieldsForm.tsx | 46 +++++++++++-------- 2 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 .changeset/olive-pears-sell.md diff --git a/.changeset/olive-pears-sell.md b/.changeset/olive-pears-sell.md new file mode 100644 index 000000000000..369f64b04dcf --- /dev/null +++ b/.changeset/olive-pears-sell.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/ui-client': patch +--- + +Fixed required custom fields considering blank spaces as valid. diff --git a/packages/ui-client/src/components/CustomFieldsForm.tsx b/packages/ui-client/src/components/CustomFieldsForm.tsx index dee63d50106e..49d4a120e47d 100644 --- a/packages/ui-client/src/components/CustomFieldsForm.tsx +++ b/packages/ui-client/src/components/CustomFieldsForm.tsx @@ -3,8 +3,9 @@ import type { SelectOption } from '@rocket.chat/fuselage'; import { Field, Select, TextInput } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useCallback, useMemo } from 'react'; import type { Control, FieldValues } from 'react-hook-form'; -import { Controller } from 'react-hook-form'; +import { Controller, useFormState, get } from 'react-hook-form'; type CustomFieldFormProps<T extends FieldValues> = { metadata: CustomFieldMetadata[]; @@ -33,32 +34,41 @@ const CustomField = <T extends FieldValues>({ ...props }: CustomFieldProps<T>) => { const t = useTranslation(); - const { getFieldState } = control; + const { errors } = useFormState({ control }); const Component = FIELD_TYPES[type] ?? null; - const selectOptions = - options.length > 0 && options[0] instanceof Array ? options : options.map((option) => [option, option, defaultValue === option]); + const selectOptions = useMemo( + () => + options.length > 0 && options[0] instanceof Array ? options : options.map((option) => [option, option, defaultValue === option]), + [defaultValue, options], + ); + + const validateRequired = useCallback((value) => (required ? typeof value === 'string' && !!value.trim() : true), [required]); - const getErrorMessage = (error: any) => { - switch (error?.type) { - case 'required': - return t('The_field_is_required', label || name); - case 'minLength': - return t('Min_length_is', props?.minLength); - case 'maxLength': - return t('Max_length_is', props?.maxLength); - } - }; + const getErrorMessage = useCallback( + (error: any) => { + switch (error?.type) { + case 'required': + return t('The_field_is_required', label || name); + case 'minLength': + return t('Min_length_is', props?.minLength); + case 'maxLength': + return t('Max_length_is', props?.maxLength); + } + }, + [label, name, props?.maxLength, props?.minLength, t], + ); - const error = getErrorMessage(getFieldState(name as any).error); + const error = get(errors, name); + const errorMessage = useMemo(() => getErrorMessage(error), [error, getErrorMessage]); return ( <Controller<T, any> name={name} control={control} defaultValue={defaultValue ?? ''} - rules={{ required, minLength: props.minLength, maxLength: props.maxLength }} + rules={{ minLength: props.minLength, maxLength: props.maxLength, validate: { required: validateRequired } }} render={({ field }) => ( <Field rcx-field-group__item> <Field.Label> @@ -66,9 +76,9 @@ const CustomField = <T extends FieldValues>({ {required && '*'} </Field.Label> <Field.Row> - <Component {...props} {...field} error={error} options={selectOptions as SelectOption[]} flexGrow={1} /> + <Component {...props} {...field} error={errorMessage} options={selectOptions as SelectOption[]} flexGrow={1} /> </Field.Row> - <Field.Error>{error}</Field.Error> + <Field.Error>{errorMessage}</Field.Error> </Field> )} /> From 6923838fe9f80e45b836b417dce365f320bd0c5b Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Sun, 2 Jul 2023 15:31:18 -0300 Subject: [PATCH 041/149] fix: livechat url triggers only fire when using the widget (#29673) --- packages/livechat/src/lib/triggers.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/livechat/src/lib/triggers.js b/packages/livechat/src/lib/triggers.js index 5f98dc2aa41d..9b630e636e67 100644 --- a/packages/livechat/src/lib/triggers.js +++ b/packages/livechat/src/lib/triggers.js @@ -57,6 +57,8 @@ const getAgent = (triggerAction) => { return agentPromise; }; +const isInIframe = () => window.self !== window.top; + class Triggers { constructor() { if (!Triggers.instance) { @@ -172,9 +174,8 @@ class Triggers { trigger.conditions.forEach((condition) => { switch (condition.name) { case 'page-url': - const { parentUrl } = store.state; const hrefRegExp = new RegExp(condition.value, 'g'); - if (parentUrl && hrefRegExp.test(parentUrl)) { + if (this.parentUrl && hrefRegExp.test(this.parentUrl)) { this.fire(trigger); } break; @@ -197,9 +198,7 @@ class Triggers { } processPageUrlTriggers() { - const { parentUrl } = store.state; - - if (!parentUrl) return; + if (!this.parentUrl) return; this._triggers.forEach((trigger) => { if (trigger.skip) return; @@ -208,7 +207,7 @@ class Triggers { if (condition.name !== 'page-url') return; const hrefRegExp = new RegExp(condition.value, 'g'); - if (hrefRegExp.test(parentUrl)) { + if (hrefRegExp.test(this.parentUrl)) { this.fire(trigger); } }); @@ -222,6 +221,10 @@ class Triggers { set enabled(value) { this._enabled = value; } + + get parentUrl() { + return isInIframe() ? store.state.parentUrl : window.location.href; + } } const instance = new Triggers(); From dbc79dd3eefe7eb766087d7783c93623659c8cff Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Mon, 3 Jul 2023 11:00:23 +0530 Subject: [PATCH 042/149] regression: Livechat UiKit button not working (#29652) --- .../meteor/server/models/raw/LivechatRooms.ts | 8 +++---- .../src/livechat/LivechatClientImpl.ts | 10 ++++++++ .../src/livechat/types/LivechatSDK.ts | 5 ++++ packages/livechat/src/lib/uiKit.js | 23 +++++++++++-------- packages/rest-typings/src/apps/index.ts | 13 +++++++++++ 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/apps/meteor/server/models/raw/LivechatRooms.ts b/apps/meteor/server/models/raw/LivechatRooms.ts index e71fbe918594..f63f12a9cc68 100644 --- a/apps/meteor/server/models/raw/LivechatRooms.ts +++ b/apps/meteor/server/models/raw/LivechatRooms.ts @@ -2044,13 +2044,13 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive 'metrics.response.ft': analyticsData.firstResponseTime, }), }, - $inc: { - ...(analyticsData && { + ...(analyticsData && { + $inc: { 'metrics.response.total': 1, 'metrics.response.tt': analyticsData.responseTime as number, 'metrics.reaction.tt': analyticsData.reactionTime as number, - }), - }, + }, + }), }; // livechat analytics : update last message timestamps diff --git a/packages/ddp-client/src/livechat/LivechatClientImpl.ts b/packages/ddp-client/src/livechat/LivechatClientImpl.ts index 8b62580e50f5..9274c25c5c26 100644 --- a/packages/ddp-client/src/livechat/LivechatClientImpl.ts +++ b/packages/ddp-client/src/livechat/LivechatClientImpl.ts @@ -324,6 +324,16 @@ export class LivechatClientImpl extends DDPSDK implements LivechatStream, Livech }); } + async sendUiInteraction( + payload: OperationParams<'POST', '/apps/ui.interaction/:id'>, + appId: string, + ): Promise<Serialized<OperationResult<'POST', '/apps/ui.interaction/:id'>>> { + if (!this.token) { + throw new Error('Invalid token'); + } + return this.rest.post(`/apps/ui.interaction/${appId}`, payload, { headers: { 'x-visitor-token': this.token } }); + } + // API DELETE deleteMessage(id: string, { rid }: { rid: string }): Promise<Serialized<OperationResult<'DELETE', '/v1/livechat/message/:_id'>>> { diff --git a/packages/ddp-client/src/livechat/types/LivechatSDK.ts b/packages/ddp-client/src/livechat/types/LivechatSDK.ts index 9e646152f4ae..171d6dfc2410 100644 --- a/packages/ddp-client/src/livechat/types/LivechatSDK.ts +++ b/packages/ddp-client/src/livechat/types/LivechatSDK.ts @@ -95,4 +95,9 @@ export interface LivechatEndpoints { id: string, args: OperationParams<'PUT', '/v1/livechat/message/:_id'>, ): Promise<Serialized<OperationResult<'PUT', '/v1/livechat/message/:_id'>>>; + + sendUiInteraction( + payload: OperationParams<'POST', '/apps/ui.interaction/:id'>, + appId: string, + ): Promise<Serialized<OperationResult<'POST', '/apps/ui.interaction/:id'>>>; } diff --git a/packages/livechat/src/lib/uiKit.js b/packages/livechat/src/lib/uiKit.js index c7cae9e4db58..dcd9d2e34a58 100644 --- a/packages/livechat/src/lib/uiKit.js +++ b/packages/livechat/src/lib/uiKit.js @@ -104,16 +104,19 @@ export const triggerAction = async ({ appId, type, actionId, rid, mid, viewId, c try { const result = await Promise.race([ - Livechat.rest.post(`/apps/ui.interaction/${appId}`, { - type, - actionId, - rid, - mid, - viewId, - container, - triggerId, - payload, - }), + Livechat.sendUiInteraction( + { + type, + actionId, + rid, + mid, + viewId, + container, + triggerId, + payload, + }, + appId, + ), new Promise((_, reject) => { setTimeout(() => { diff --git a/packages/rest-typings/src/apps/index.ts b/packages/rest-typings/src/apps/index.ts index c70b8d7b60d7..76ad596bb90b 100644 --- a/packages/rest-typings/src/apps/index.ts +++ b/packages/rest-typings/src/apps/index.ts @@ -251,4 +251,17 @@ export type AppsEndpoints = { app: App; }; }; + + '/apps/ui.interaction/:id': { + POST: (params: { + type: string; + actionId: string; + rid: string; + mid: string; + viewId: string; + container: string; + triggerId: string; + payload: any; + }) => any; + }; }; From eecd9fc99a6a3d7f6156f9c6eaed5db64bba991a Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Mon, 3 Jul 2023 12:33:56 +0530 Subject: [PATCH 043/149] fix: Omnichannel Tags available to be used in the wrong department (#29169) Co-authored-by: Martin Schoeler <20868078+MartinSchoeler@users.noreply.github.com> --- .changeset/great-brooms-invent.md | 6 ++ .../client/components/Omnichannel/Tags.tsx | 12 ++- .../Omnichannel/hooks/useLivechatTags.ts | 19 ++++ .../Omnichannel/modals/CloseChatModal.tsx | 2 +- .../server/lib/canned-responses.js | 64 ++----------- .../server/api/lib/departments.ts | 38 ++++++++ .../server/api/lib/tags.ts | 49 +++++++++- .../livechat-enterprise/server/api/tags.ts | 4 +- apps/meteor/ee/client/hooks/useTagsList.ts | 4 +- .../tags/AutoCompleteTagsMultiple.js | 4 +- .../omnichannel/tags/CurrentChatTags.tsx | 4 +- .../ee/client/omnichannel/tags/TagsTable.tsx | 1 + .../ee/server/models/raw/LivechatUnit.ts | 2 +- .../server/models/raw/LivechatDepartment.ts | 43 ++++++++- apps/meteor/tests/data/livechat/tags.ts | 26 +++++- .../tests/end-to-end/api/livechat/13-tags.ts | 92 +++++++++++++++++-- .../src/models/ILivechatDepartmentModel.ts | 1 + packages/rest-typings/src/v1/omnichannel.ts | 11 ++- 18 files changed, 301 insertions(+), 81 deletions(-) create mode 100644 .changeset/great-brooms-invent.md create mode 100644 apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts create mode 100644 apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts diff --git a/.changeset/great-brooms-invent.md b/.changeset/great-brooms-invent.md new file mode 100644 index 000000000000..8dc820e8c839 --- /dev/null +++ b/.changeset/great-brooms-invent.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/rest-typings": patch +--- + +fix: Omnichannel Tags available to be used in the wrong department diff --git a/apps/meteor/client/components/Omnichannel/Tags.tsx b/apps/meteor/client/components/Omnichannel/Tags.tsx index dfa1cb713a5f..3074639cee0d 100644 --- a/apps/meteor/client/components/Omnichannel/Tags.tsx +++ b/apps/meteor/client/components/Omnichannel/Tags.tsx @@ -1,23 +1,25 @@ import { Field, TextInput, Chip, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; -import { useQuery } from '@tanstack/react-query'; +import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ChangeEvent, ReactElement } from 'react'; import React, { useState } from 'react'; import { useFormsSubscription } from '../../views/omnichannel/additionalForms'; import { FormSkeleton } from './Skeleton'; +import { useLivechatTags } from './hooks/useLivechatTags'; const Tags = ({ tags = [], handler, error, tagRequired, + department, }: { tags?: string[]; handler: (value: string[]) => void; error?: string; tagRequired?: boolean; + department?: string; }): ReactElement => { const t = useTranslation(); const forms = useFormsSubscription() as any; @@ -27,9 +29,8 @@ const Tags = ({ // Conditional hook was required since the whole formSubscription uses hooks in an incorrect manner const EETagsComponent = useCurrentChatTags?.(); - const getTags = useEndpoint('GET', '/v1/livechat/tags'); - const { data: tagsResult, isInitialLoading } = useQuery(['/v1/livechat/tags'], () => getTags({ text: '' }), { - enabled: Boolean(EETagsComponent), + const { data: tagsResult, isInitialLoading } = useLivechatTags({ + department, }); const dispatchToastMessage = useToastMessageDispatch(); @@ -81,6 +82,7 @@ const Tags = ({ handler(tags.map((tag) => tag.label)); handlePaginatedTagValue(tags); }} + department={department} /> </Field.Row> ) : ( diff --git a/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts new file mode 100644 index 000000000000..ac042274a2c7 --- /dev/null +++ b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts @@ -0,0 +1,19 @@ +import { useEndpoint } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; + +type Props = { + department?: string; + text?: string; +}; + +export const useLivechatTags = (options: Props) => { + const getTags = useEndpoint('GET', '/v1/livechat/tags'); + + const { department, text } = options; + return useQuery(['/v1/livechat/tags', text, department], () => + getTags({ + text: text || '', + ...(department && { department }), + }), + ); +}; diff --git a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx index fd145ea9d9e6..c8307e2045f9 100644 --- a/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/CloseChatModal.tsx @@ -150,7 +150,7 @@ const CloseChatModal = ({ <Field.Error>{errors.comment?.message}</Field.Error> </Field> <Field> - <Tags tagRequired={tagRequired} tags={tags} handler={handleTags} /> + <Tags tagRequired={tagRequired} tags={tags} handler={handleTags} {...(department && { department: department._id })} /> <Field.Error>{errors.tags?.message}</Field.Error> </Field> {canSendTranscript && ( diff --git a/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js b/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js index 26f751fe4e99..9f0872c38f29 100644 --- a/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js +++ b/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js @@ -1,7 +1,8 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { LivechatDepartmentAgents, CannedResponse, LivechatUnit } from '@rocket.chat/models'; +import { CannedResponse } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../../../app/authorization/server/functions/hasPermission'; +import { getDepartmentsWhichUserCanAccess } from '../../../livechat-enterprise/server/api/lib/departments'; export async function findAllCannedResponses({ userId }) { // If the user is an admin or livechat manager, get his own responses and all responses from all departments @@ -30,23 +31,8 @@ export async function findAllCannedResponses({ userId }) { }).toArray(); } - // Last scenario: user is an agente, so get his own responses and those from the departments he is in - const departments = await LivechatDepartmentAgents.find( - { - agentId: userId, - }, - { - projection: { - departmentId: 1, - }, - }, - ).toArray(); - - const monitoredDepartments = await LivechatUnit.findMonitoredDepartmentsByMonitorId(userId); - const combinedDepartments = [ - ...departments.map((department) => department.departmentId), - ...monitoredDepartments.map((department) => department._id), - ]; + // Last scenario: user is an agent, so get his own responses and those from the departments he is in + const accessibleDepartments = await getDepartmentsWhichUserCanAccess(userId); return CannedResponse.find({ $or: [ @@ -57,7 +43,7 @@ export async function findAllCannedResponses({ userId }) { { scope: 'department', departmentId: { - $in: combinedDepartments, + $in: accessibleDepartments, }, }, { @@ -71,26 +57,11 @@ export async function findAllCannedResponsesFilter({ userId, shortcut, text, dep let extraFilter = []; // if user cannot see all, filter to private + public + departments user is in if (!(await hasPermissionAsync(userId, 'view-all-canned-responses'))) { - const departments = await LivechatDepartmentAgents.find( - { - agentId: userId, - }, - { - fields: { - departmentId: 1, - }, - }, - ).toArray(); - - const monitoredDepartments = await LivechatUnit.findMonitoredDepartmentsByMonitorId(userId); - const combinedDepartments = [ - ...departments.map((department) => department.departmentId), - ...monitoredDepartments.map((department) => department._id), - ]; + const accessibleDepartments = await getDepartmentsWhichUserCanAccess(userId); - const isDepartmentInScope = (departmentId) => !!combinedDepartments.includes(departmentId); + const isDepartmentInScope = (departmentId) => !!accessibleDepartments.includes(departmentId); - const departmentIds = departmentId && isDepartmentInScope(departmentId) ? [departmentId] : combinedDepartments; + const departmentIds = departmentId && isDepartmentInScope(departmentId) ? [departmentId] : accessibleDepartments; extraFilter = [ { @@ -163,22 +134,7 @@ export async function findOneCannedResponse({ userId, _id }) { return CannedResponse.findOneById(_id); } - const departments = await LivechatDepartmentAgents.find( - { - agentId: userId, - }, - { - fields: { - departmentId: 1, - }, - }, - ).toArray(); - - const monitoredDepartments = await LivechatUnit.findMonitoredDepartmentsByMonitorId(userId); - const combinedDepartments = [ - ...departments.map((department) => department.departmentId), - ...monitoredDepartments.map((department) => department._id), - ]; + const accessibleDepartments = await getDepartmentsWhichUserCanAccess(userId); const filter = { _id, @@ -190,7 +146,7 @@ export async function findOneCannedResponse({ userId, _id }) { { scope: 'department', departmentId: { - $in: combinedDepartments, + $in: accessibleDepartments, }, }, { diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts new file mode 100644 index 000000000000..2e86aececc58 --- /dev/null +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts @@ -0,0 +1,38 @@ +import { LivechatDepartment, LivechatDepartmentAgents, LivechatUnit } from '@rocket.chat/models'; + +import { helperLogger } from '../../lib/logger'; + +export const getDepartmentsWhichUserCanAccess = async (userId: string): Promise<string[]> => { + const departments = await LivechatDepartmentAgents.find( + { + agentId: userId, + }, + { + projection: { + departmentId: 1, + }, + }, + ).toArray(); + + const monitoredDepartments = await LivechatUnit.findMonitoredDepartmentsByMonitorId(userId); + const combinedDepartments = [ + ...departments.map((department) => department.departmentId), + ...monitoredDepartments.map((department) => department._id), + ]; + + return [...new Set(combinedDepartments)]; +}; + +export const hasAccessToDepartment = async (userId: string, departmentId: string): Promise<boolean> => { + const department = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(userId, departmentId); + if (department) { + helperLogger.debug(`User ${userId} has access to department ${departmentId} because they are an agent`); + return true; + } + + const monitorAccess = await LivechatDepartment.checkIfMonitorIsMonitoringDepartmentById(userId, departmentId); + helperLogger.debug( + `User ${userId} ${monitorAccess ? 'has' : 'does not have'} access to department ${departmentId} because they are a monitor`, + ); + return monitorAccess; +}; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/lib/tags.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/lib/tags.ts index 0ebf4df1553e..7f0020604123 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/lib/tags.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/lib/tags.ts @@ -1,7 +1,10 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import { LivechatTag } from '@rocket.chat/models'; import type { ILivechatTag } from '@rocket.chat/core-typings'; -import type { FindOptions } from 'mongodb'; +import type { Filter, FindOptions } from 'mongodb'; + +import { hasPermissionAsync } from '../../../../../../app/authorization/server/functions/hasPermission'; +import { hasAccessToDepartment } from './departments'; type FindTagsParams = { userId: string; @@ -11,6 +14,8 @@ type FindTagsParams = { count: number; sort: FindOptions<ILivechatTag>['sort']; }; + department?: string; + viewAll?: boolean; }; type FindTagsResult = { @@ -27,11 +32,47 @@ type FindTagsByIdParams = { type FindTagsByIdResult = ILivechatTag | null; -export async function findTags({ text, pagination: { offset, count, sort } }: FindTagsParams): Promise<FindTagsResult> { - const query = { - ...(text && { $or: [{ name: new RegExp(escapeRegExp(text), 'i') }, { description: new RegExp(escapeRegExp(text), 'i') }] }), +// If viewAll is true, then all tags will be returned, regardless of department +// If viewAll is false, then all public tags will be returned, and +// if department is specified, then all department tags will be returned +export async function findTags({ + userId, + text, + department, + viewAll, + pagination: { offset, count, sort }, +}: FindTagsParams): Promise<FindTagsResult> { + if (!(await hasPermissionAsync(userId, 'manage-livechat-tags'))) { + if (viewAll) { + viewAll = false; + } + + if (department) { + if (!(await hasAccessToDepartment(userId, department))) { + department = undefined; + } + } + } + + const query: { + $and?: Filter<ILivechatTag>[]; + } = { + $and: [ + ...(text ? [{ $or: [{ name: new RegExp(escapeRegExp(text), 'i') }, { description: new RegExp(escapeRegExp(text), 'i') }] }] : []), + ...(!viewAll + ? [ + { + $or: [{ departments: { $size: 0 } }, ...(department ? [{ departments: department }] : [])], + }, + ] + : []), + ], }; + if (!query?.$and?.length) { + delete query.$and; + } + const { cursor, totalCount } = LivechatTag.findPaginated(query, { sort: sort || { name: 1 }, skip: offset, diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/tags.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/tags.ts index 19ef9cf922dc..4c84a42bd9e6 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/tags.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/tags.ts @@ -9,12 +9,14 @@ API.v1.addRoute( async get() { const { offset, count } = await getPaginationItems(this.queryParams); const { sort } = await this.parseJsonQuery(); - const { text } = this.queryParams; + const { text, viewAll, department } = this.queryParams; return API.v1.success( await findTags({ userId: this.userId, text, + department, + viewAll: viewAll === 'true', pagination: { offset, count, diff --git a/apps/meteor/ee/client/hooks/useTagsList.ts b/apps/meteor/ee/client/hooks/useTagsList.ts index 5c887d28d713..013a320fbade 100644 --- a/apps/meteor/ee/client/hooks/useTagsList.ts +++ b/apps/meteor/ee/client/hooks/useTagsList.ts @@ -8,6 +8,7 @@ import { RecordList } from '../../../client/lib/lists/RecordList'; type TagsListOptions = { filter: string; + department?: string; }; export const useTagsList = ( @@ -33,6 +34,7 @@ export const useTagsList = ( text: options.filter, offset: start, count: end + start, + ...(options.department && { department: options.department }), }); return { items: tags.map((tag: any) => { @@ -44,7 +46,7 @@ export const useTagsList = ( itemCount: total, }; }, - [getTags, options.filter], + [getTags, options.filter, options.department], ); const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25); diff --git a/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js b/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js index 2ae6f893f812..5f7fe4a4ffe6 100644 --- a/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js +++ b/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js @@ -8,7 +8,7 @@ import { AsyncStatePhase } from '../../../../client/hooks/useAsyncState'; import { useTagsList } from '../../hooks/useTagsList'; const AutoCompleteTagMultiple = (props) => { - const { value, onlyMyTags = false, onChange = () => {} } = props; + const { value, onlyMyTags = false, onChange = () => {}, department } = props; const t = useTranslation(); const [tagsFilter, setTagsFilter] = useState(''); @@ -16,7 +16,7 @@ const AutoCompleteTagMultiple = (props) => { const debouncedTagsFilter = useDebouncedValue(tagsFilter, 500); const { itemsList: tagsList, loadMoreItems: loadMoreTags } = useTagsList( - useMemo(() => ({ filter: debouncedTagsFilter, onlyMyTags }), [debouncedTagsFilter, onlyMyTags]), + useMemo(() => ({ filter: debouncedTagsFilter, onlyMyTags, department }), [debouncedTagsFilter, onlyMyTags, department]), ); const { phase: tagsPhase, items: tagsItems, itemCount: tagsTotal } = useRecordList(tagsList); diff --git a/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx b/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx index 30bae3323231..7253764c52b6 100644 --- a/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx +++ b/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx @@ -3,8 +3,8 @@ import React from 'react'; import AutoCompleteTagsMultiple from './AutoCompleteTagsMultiple'; -const CurrentChatTags: FC<{ value: Array<string>; handler: () => void }> = ({ value, handler }) => ( - <AutoCompleteTagsMultiple onChange={handler} value={value} /> +const CurrentChatTags: FC<{ value: Array<string>; handler: () => void; department?: string }> = ({ value, handler, department }) => ( + <AutoCompleteTagsMultiple onChange={handler} value={value} department={department} /> ); export default CurrentChatTags; diff --git a/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx b/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx index 98f2220fe5bf..01ab1f0ec637 100644 --- a/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/tags/TagsTable.tsx @@ -39,6 +39,7 @@ const TagsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { const query = useMemo( () => ({ + viewAll: 'true' as const, fields: JSON.stringify({ name: 1 }), text: filter, sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }), diff --git a/apps/meteor/ee/server/models/raw/LivechatUnit.ts b/apps/meteor/ee/server/models/raw/LivechatUnit.ts index c0f32af57121..2006f8200767 100644 --- a/apps/meteor/ee/server/models/raw/LivechatUnit.ts +++ b/apps/meteor/ee/server/models/raw/LivechatUnit.ts @@ -202,7 +202,7 @@ export class LivechatUnitRaw extends BaseRaw<IOmnichannelBusinessUnit> implement async findMonitoredDepartmentsByMonitorId(monitorId: string): Promise<ILivechatDepartment[]> { const monitoredUnits = await this.findByMonitorId(monitorId); - return LivechatDepartment.findByUnitIds(monitoredUnits, {}).toArray(); + return LivechatDepartment.findActiveByUnitIds(monitoredUnits, {}).toArray(); } countUnits(): Promise<number> { diff --git a/apps/meteor/server/models/raw/LivechatDepartment.ts b/apps/meteor/server/models/raw/LivechatDepartment.ts index 75329e3e6125..5b1b4010dd38 100644 --- a/apps/meteor/server/models/raw/LivechatDepartment.ts +++ b/apps/meteor/server/models/raw/LivechatDepartment.ts @@ -13,7 +13,7 @@ import type { UpdateFilter, } from 'mongodb'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { LivechatDepartmentAgents } from '@rocket.chat/models'; +import { LivechatDepartmentAgents, LivechatUnitMonitors } from '@rocket.chat/models'; import { BaseRaw } from './BaseRaw'; @@ -353,6 +353,47 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen return this.find(query, options); } + + checkIfMonitorIsMonitoringDepartmentById(monitorId: string, departmentId: string): Promise<boolean> { + const aggregation = [ + { + $match: { + enabled: true, + _id: departmentId, + }, + }, + { + $lookup: { + from: LivechatUnitMonitors.getCollectionName(), + localField: 'parentId', + foreignField: 'unitId', + as: 'monitors', + pipeline: [ + { + $match: { + monitorId, + }, + }, + ], + }, + }, + { + $match: { + monitors: { + $exists: true, + $ne: [], + }, + }, + }, + { + $project: { + _id: 1, + }, + }, + ]; + + return this.col.aggregate(aggregation).hasNext(); + } } const difference = <T>(arr: T[], arr2: T[]): T[] => { diff --git a/apps/meteor/tests/data/livechat/tags.ts b/apps/meteor/tests/data/livechat/tags.ts index c8cfbd1d4bed..4ba5c78025cd 100644 --- a/apps/meteor/tests/data/livechat/tags.ts +++ b/apps/meteor/tests/data/livechat/tags.ts @@ -3,7 +3,7 @@ import type { ILivechatTag } from '@rocket.chat/core-typings'; import { credentials, methodCall, request } from '../api-data'; import type { DummyResponse } from './utils'; -export const saveTags = (): Promise<ILivechatTag> => { +export const saveTags = (departments: string[] = []): Promise<ILivechatTag> => { return new Promise((resolve, reject) => { request .post(methodCall(`livechat:saveTag`)) @@ -11,7 +11,29 @@ export const saveTags = (): Promise<ILivechatTag> => { .send({ message: JSON.stringify({ method: 'livechat:saveTag', - params: [undefined, { name: faker.person.firstName(), description: faker.lorem.sentence() }, []], + params: [undefined, { name: faker.person.firstName(), description: faker.lorem.sentence() }, departments], + id: '101', + msg: 'method', + }), + }) + .end((err: Error, res: DummyResponse<string, 'wrapped'>) => { + if (err) { + return reject(err); + } + resolve(JSON.parse(res.body.message).result); + }); + }); +}; + +export const removeTag = (id: string): Promise<void> => { + return new Promise((resolve, reject) => { + request + .post(methodCall(`livechat:removeTag`)) + .set(credentials) + .send({ + message: JSON.stringify({ + method: 'livechat:removeTag', + params: [id], id: '101', msg: 'method', }), diff --git a/apps/meteor/tests/end-to-end/api/livechat/13-tags.ts b/apps/meteor/tests/end-to-end/api/livechat/13-tags.ts index 986e268e26f4..a08e77238caa 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/13-tags.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/13-tags.ts @@ -1,11 +1,13 @@ /* eslint-env mocha */ import { expect } from 'chai'; +import type { ILivechatDepartment, ILivechatTag } from '@rocket.chat/core-typings'; import { getCredentials, api, request, credentials } from '../../../data/api-data'; -import { saveTags } from '../../../data/livechat/tags'; +import { removeTag, saveTags } from '../../../data/livechat/tags'; import { updatePermission, updateSetting } from '../../../data/permissions.helper'; import { IS_EE } from '../../../e2e/config/constants'; +import { createDepartment } from '../../../data/livechat/rooms'; (IS_EE ? describe : describe.skip)('[EE] Livechat - Tags', function () { this.retries(0); @@ -17,26 +19,104 @@ import { IS_EE } from '../../../e2e/config/constants'; }); describe('livechat/tags', () => { + let tagsData: { + caseA: { department: ILivechatDepartment; tag: ILivechatTag }; + caseB: { department: ILivechatDepartment; tag: ILivechatTag }; + casePublic: { tag: ILivechatTag }; + }; it('should throw unauthorized error when the user does not have the necessary permission', async () => { await updatePermission('manage-livechat-tags', []); await updatePermission('view-l-room', []); const response = await request.get(api('livechat/tags')).set(credentials).expect('Content-Type', 'application/json').expect(403); expect(response.body).to.have.property('success', false); + + await updatePermission('manage-livechat-tags', ['admin']); + await updatePermission('view-l-room', ['livechat-agent', 'livechat-manager', 'admin']); + }); + it('should remove all existing tags', async () => { + const allTags = await request + .get(api('livechat/tags')) + .set(credentials) + .query({ viewAll: 'true' }) + .expect('Content-Type', 'application/json') + .expect(200); + + const { tags } = allTags.body; + for await (const tag of tags) { + await removeTag(tag._id); + } + + const response = await request.get(api('livechat/tags')).set(credentials).expect('Content-Type', 'application/json').expect(200); + + expect(response.body).to.have.property('success', true); + expect(response.body).to.have.property('tags').and.to.be.an('array').that.is.empty; + }); + it('should add 3 tags', async () => { + const dA = await createDepartment(); + const tagA = await saveTags([dA._id]); + + const dB = await createDepartment(); + const tagB = await saveTags([dB._id]); + + const publicTag = await saveTags(); + + tagsData = { + caseA: { department: dA, tag: tagA }, + caseB: { department: dB, tag: tagB }, + casePublic: { tag: publicTag }, + }; }); it('should return an array of tags', async () => { - await updatePermission('manage-livechat-tags', ['admin']); - await updatePermission('view-l-room', ['livechat-agent']); - const tag = await saveTags(); + const { tag } = tagsData.caseA; const response = await request .get(api('livechat/tags')) .set(credentials) - .query({ text: tag.name }) + .query({ text: tag.name, viewAll: 'true' }) .expect('Content-Type', 'application/json') .expect(200); expect(response.body).to.have.property('success', true); - expect(response.body.tags).to.be.an('array').with.lengthOf.greaterThan(0); + expect(response.body.tags).to.be.an('array').with.lengthOf(1); expect(response.body.tags[0]).to.have.property('_id', tag._id); }); + it('show return all tags when "viewAll" param is true', async () => { + const response = await request + .get(api('livechat/tags')) + .set(credentials) + .query({ viewAll: 'true' }) + .expect('Content-Type', 'application/json') + .expect(200); + + expect(response.body).to.have.property('success', true); + expect(response.body.tags).to.be.an('array').with.lengthOf(3); + + const expectedTags = [tagsData.caseA.tag, tagsData.caseB.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort(); + const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort(); + expect(actualTags).to.deep.equal(expectedTags); + }); + it('should return department tags and public tags when "departmentId" param is provided', async () => { + const { department } = tagsData.caseA; + + const response = await request + .get(api('livechat/tags')) + .set(credentials) + .query({ department: department._id }) + .expect('Content-Type', 'application/json') + .expect(200); + + expect(response.body).to.have.property('success', true); + expect(response.body.tags).to.be.an('array').with.lengthOf(2); + + const expectedTags = [tagsData.caseA.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort(); + const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort(); + expect(actualTags).to.deep.equal(expectedTags); + }); + it('should return public tags when "departmentId" param is not provided', async () => { + const response = await request.get(api('livechat/tags')).set(credentials).expect('Content-Type', 'application/json').expect(200); + + expect(response.body).to.have.property('success', true); + expect(response.body.tags).to.be.an('array').with.lengthOf(1); + expect(response.body.tags[0]).to.have.property('_id', tagsData.casePublic.tag._id); + }); }); describe('livechat/tags/:tagId', () => { diff --git a/packages/model-typings/src/models/ILivechatDepartmentModel.ts b/packages/model-typings/src/models/ILivechatDepartmentModel.ts index d0f11f2bff95..800800878088 100644 --- a/packages/model-typings/src/models/ILivechatDepartmentModel.ts +++ b/packages/model-typings/src/models/ILivechatDepartmentModel.ts @@ -59,4 +59,5 @@ export interface ILivechatDepartmentModel extends IBaseModel<ILivechatDepartment findOneByIdOrName(_idOrName: string, options?: FindOptions<ILivechatDepartment>): Promise<ILivechatDepartment | null>; findByUnitIds(unitIds: string[], options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>; findActiveByUnitIds(unitIds: string[], options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>; + checkIfMonitorIsMonitoringDepartmentById(monitorId: string, departmentId: string): Promise<boolean>; } diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index 8423c4db1fdd..257706a0d270 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -463,7 +463,7 @@ const LivechatMonitorsListSchema = { export const isLivechatMonitorsListProps = ajv.compile<LivechatMonitorsListProps>(LivechatMonitorsListSchema); -type LivechatTagsListProps = PaginatedRequest<{ text: string }, 'name'>; +type LivechatTagsListProps = PaginatedRequest<{ text: string; viewAll?: 'true' | 'false'; department?: string }, 'name'>; const LivechatTagsListSchema = { type: 'object', @@ -471,6 +471,15 @@ const LivechatTagsListSchema = { text: { type: 'string', }, + department: { + type: 'string', + nullable: true, + }, + viewAll: { + type: 'string', + nullable: true, + enum: ['true', 'false'], + }, count: { type: 'number', nullable: true, From 74aa6770881eb620a2275b84c55465d7552e4597 Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Mon, 3 Jul 2023 10:31:23 -0300 Subject: [PATCH 044/149] feat: Add custom OAuth setting to allow merging users to others from distinct services (#28783) * Merge OAuth users with existing users from distinct services when setting is enabled * Add changesets * Fix changesets * Apply requested changes --------- Co-authored-by: Diego Sampaio <chinello@gmail.com> Co-authored-by: Rafael Tapia <rafael.tapia@rocket.chat> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Gabriel Casals <83978645+casalsgh@users.noreply.github.com> --- .changeset/dirty-singers-promise.md | 6 +++++ .../server/custom_oauth_server.js | 9 +++++-- .../lib/server/functions/addOAuthService.ts | 12 +++++++++ .../lib/server/startup/oAuthServicesUpdate.js | 3 +++ .../rocketchat-i18n/i18n/en.i18n.json | 2 ++ apps/meteor/server/startup/migrations/v300.ts | 27 +++++++++++++++++++ packages/rest-typings/src/v1/settings.ts | 1 + 7 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 .changeset/dirty-singers-promise.md create mode 100644 apps/meteor/server/startup/migrations/v300.ts diff --git a/.changeset/dirty-singers-promise.md b/.changeset/dirty-singers-promise.md new file mode 100644 index 000000000000..62660283d8d4 --- /dev/null +++ b/.changeset/dirty-singers-promise.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/rest-typings": minor +--- + +feat: Add custom OAuth setting to allow merging users to others from distinct services diff --git a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js index 01c8d1bc00e9..abfdafed6f53 100644 --- a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js +++ b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js @@ -81,6 +81,7 @@ export class CustomOAuth { this.nameField = (options.nameField || '').trim(); this.avatarField = (options.avatarField || '').trim(); this.mergeUsers = options.mergeUsers; + this.mergeUsersDistinctServices = options.mergeUsersDistinctServices; this.rolesClaim = options.rolesClaim || 'roles'; this.accessTokenParam = options.accessTokenParam; this.channelsAdmin = options.channelsAdmin || 'rocket.cat'; @@ -333,9 +334,13 @@ export class CustomOAuth { let user = undefined; if (this.keyField === 'username') { - user = await Users.findOneByUsernameAndServiceNameIgnoringCase(serviceData.username, serviceData._id, serviceName); + user = this.mergeUsersDistinctServices + ? await Users.findOneByUsernameIgnoringCase(serviceData.username) + : await Users.findOneByUsernameAndServiceNameIgnoringCase(serviceData.username, serviceData.id, serviceName); } else if (this.keyField === 'email') { - user = await Users.findOneByEmailAddressAndServiceNameIgnoringCase(serviceData.email, serviceData._id, serviceName); + user = this.mergeUsersDistinctServices + ? await Users.findOneByEmailAddress(serviceData.email) + : await Users.findOneByEmailAddressAndServiceNameIgnoringCase(serviceData.email, serviceData.id, serviceName); } if (!user) { diff --git a/apps/meteor/app/lib/server/functions/addOAuthService.ts b/apps/meteor/app/lib/server/functions/addOAuthService.ts index 0c206a4d1544..eb28c5a7e3eb 100644 --- a/apps/meteor/app/lib/server/functions/addOAuthService.ts +++ b/apps/meteor/app/lib/server/functions/addOAuthService.ts @@ -230,6 +230,18 @@ export async function addOAuthService(name: string, values: { [k: string]: strin i18nLabel: 'Accounts_OAuth_Custom_Merge_Users', persistent: true, }); + await settingsRegistry.add(`Accounts_OAuth_Custom-${name}-merge_users_distinct_services`, values.mergeUsersDistinctServices || false, { + type: 'boolean', + group: 'OAuth', + section: `Custom OAuth: ${name}`, + i18nLabel: 'Accounts_OAuth_Custom_Merge_Users_Distinct_Services', + i18nDescription: 'Accounts_OAuth_Custom_Merge_Users_Distinct_Services_Description', + enableQuery: { + _id: `Accounts_OAuth_Custom-${name}-merge_users`, + value: true, + }, + persistent: true, + }); await settingsRegistry.add(`Accounts_OAuth_Custom-${name}-show_button`, values.showButton || true, { type: 'boolean', group: 'OAuth', diff --git a/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js b/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js index 58e822270524..e69d3c900ead 100644 --- a/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js +++ b/apps/meteor/app/lib/server/startup/oAuthServicesUpdate.js @@ -53,6 +53,7 @@ async function _OAuthServicesUpdate() { data.channelsMap = settings.get(`${key}-groups_channel_map`); data.channelsAdmin = settings.get(`${key}-channels_admin`); data.mergeUsers = settings.get(`${key}-merge_users`); + data.mergeUsersDistinctServices = settings.get(`${key}-merge_users_distinct_services`); data.mapChannels = settings.get(`${key}-map_channels`); data.mergeRoles = settings.get(`${key}-merge_roles`); data.rolesToSync = settings.get(`${key}-roles_to_sync`); @@ -78,6 +79,7 @@ async function _OAuthServicesUpdate() { channelsMap: data.channelsMap, channelsAdmin: data.channelsAdmin, mergeUsers: data.mergeUsers, + mergeUsersDistinctServices: data.mergeUsersDistinctServices, mergeRoles: data.mergeRoles, rolesToSync: data.rolesToSync, accessTokenParam: data.accessTokenParam, @@ -187,6 +189,7 @@ async function customOAuthServicesInit() { channelsMap: process.env[`${serviceKey}_groups_channel_map`], channelsAdmin: process.env[`${serviceKey}_channels_admin`], mergeUsers: process.env[`${serviceKey}_merge_users`] === 'true', + mergeUsersDistinctServices: process.env[`${serviceKey}_merge_users_distinct_services`] === 'true', mapChannels: process.env[`${serviceKey}_map_channels`], mergeRoles: process.env[`${serviceKey}_merge_roles`] === 'true', rolesToSync: process.env[`${serviceKey}_roles_to_sync`], diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 3690cbb353d7..60b02e3ca684 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -135,6 +135,8 @@ "Accounts_OAuth_Custom_Map_Channels": "Map Roles/Groups to channels", "Accounts_OAuth_Custom_Merge_Roles": "Merge Roles from SSO", "Accounts_OAuth_Custom_Merge_Users": "Merge users", + "Accounts_OAuth_Custom_Merge_Users_Distinct_Services": "Merge users from distinct services", + "Accounts_OAuth_Custom_Merge_Users_Distinct_Services_Description": "When the given key field matches the one of an existing user, allow users from this OAuth service to be merged to existing users regardless of their origin service.", "Accounts_OAuth_Custom_Name_Field": "Name field", "Accounts_OAuth_Custom_Roles_Claim": "Roles/Groups field name", "Accounts_OAuth_Custom_Roles_To_Sync": "Roles to Sync", diff --git a/apps/meteor/server/startup/migrations/v300.ts b/apps/meteor/server/startup/migrations/v300.ts new file mode 100644 index 000000000000..4e00b2c03075 --- /dev/null +++ b/apps/meteor/server/startup/migrations/v300.ts @@ -0,0 +1,27 @@ +import { Settings } from '@rocket.chat/models'; + +import { settingsRegistry } from '../../../app/settings/server'; +import { addMigration } from '../../lib/migrations'; + +addMigration({ + version: 300, + async up() { + const customOauthServices = await Settings.find({ _id: /Accounts_OAuth_Custom-[^-]+$/ }, { projection: { _id: 1 } }).toArray(); + const serviceNames = customOauthServices.map(({ _id }) => _id.replace('Accounts_OAuth_Custom-', '')); + + for await (const serviceName of serviceNames) { + await settingsRegistry.add(`Accounts_OAuth_Custom-${serviceName}-merge_users_distinct_services`, false, { + type: 'boolean', + group: 'OAuth', + section: `Custom OAuth: ${serviceName}`, + i18nLabel: 'Accounts_OAuth_Custom_Merge_Users_Distinct_Services', + i18nDescription: 'Accounts_OAuth_Custom_Merge_Users_Distinct_Services_Description', + enableQuery: { + _id: `Accounts_OAuth_Custom-${serviceName}-merge_users`, + value: true, + }, + persistent: true, + }); + } + }, +}); diff --git a/packages/rest-typings/src/v1/settings.ts b/packages/rest-typings/src/v1/settings.ts index 694ea9f59318..cbc789e22051 100644 --- a/packages/rest-typings/src/v1/settings.ts +++ b/packages/rest-typings/src/v1/settings.ts @@ -32,6 +32,7 @@ export type OauthCustomConfiguration = { channelsMap: string; channelsAdmin: string; mergeUsers: boolean; + mergeUsersDistinctServices: boolean; mergeRoles: boolean; accessTokenParam: string; showButton: boolean; From 649347846dc3a6b4fd99fca5a87fa72380119b67 Mon Sep 17 00:00:00 2001 From: Hugo Costa <hugocarreiracosta@gmail.com> Date: Mon, 3 Jul 2023 11:20:44 -0300 Subject: [PATCH 045/149] chore: Device Management version info at table and contextual bar (#29606) --- .../DeviceManagementAdminRow.tsx | 3 +++ .../DeviceManagementAdminTable.tsx | 2 ++ .../DeviceManagementInfo/DeviceManagementInfo.tsx | 7 ++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx index cb7aae0ba80a..200eb40686e4 100644 --- a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx +++ b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminRow.tsx @@ -17,6 +17,7 @@ type DeviceRowProps = { deviceType?: string; deviceOSName?: string; loginAt: string; + rcVersion?: string; onReload: () => void; }; @@ -28,6 +29,7 @@ const DeviceManagementAdminRow = ({ deviceType = 'browser', deviceOSName = '', loginAt, + rcVersion, onReload, }: DeviceRowProps): ReactElement => { const t = useTranslation(); @@ -69,6 +71,7 @@ const DeviceManagementAdminRow = ({ {deviceName && <Box withTruncatedText>{deviceName}</Box>} </Box> </GenericTableCell> + <GenericTableCell>{rcVersion}</GenericTableCell> <GenericTableCell>{deviceOSName}</GenericTableCell> <GenericTableCell withTruncatedText>{username}</GenericTableCell> {mediaQuery && <GenericTableCell>{formatDateAndTime(loginAt)}</GenericTableCell>} diff --git a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx index c9e94518cf08..db1c78f53ced 100644 --- a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx +++ b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx @@ -55,6 +55,7 @@ const DeviceManagementAdminTable = ({ reloadRef }: { reloadRef: MutableRefObject <GenericTableHeaderCell key={'client'} direction={sortDirection} active={sortBy === 'client'} onClick={setSort} sort='client'> {t('Client')} </GenericTableHeaderCell>, + <GenericTableHeaderCell key={'rcVersion'}>{t('Version')}</GenericTableHeaderCell>, <GenericTableHeaderCell key={'os'} direction={sortDirection} active={sortBy === 'os'} onClick={setSort} sort='os'> {t('OS')} </GenericTableHeaderCell>, @@ -91,6 +92,7 @@ const DeviceManagementAdminTable = ({ reloadRef }: { reloadRef: MutableRefObject deviceName={session?.device?.name} deviceType={session?.device?.type} deviceOSName={session?.device?.os?.name} + rcVersion={session?.device?.version} loginAt={session.loginAt} onReload={reload} /> diff --git a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementInfo/DeviceManagementInfo.tsx b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementInfo/DeviceManagementInfo.tsx index 4065057ecea7..89f1af7132a1 100644 --- a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementInfo/DeviceManagementInfo.tsx +++ b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementInfo/DeviceManagementInfo.tsx @@ -29,7 +29,7 @@ const DeviceManagementInfo = ({ device, sessionId, loginAt, ip, userId, _user, o const handleDeviceLogout = useDeviceLogout(sessionId, '/v1/sessions/logout'); - const { name: clientName, os } = device || {}; + const { name: clientName, os, version: rcVersion } = device || {}; const { username, name } = _user || {}; const userPresence = usePresence(userId); @@ -48,6 +48,11 @@ const DeviceManagementInfo = ({ device, sessionId, loginAt, ip, userId, _user, o <InfoPanel.Text>{clientName}</InfoPanel.Text> </InfoPanel.Field> + <InfoPanel.Field> + <InfoPanel.Label>{t('Version')}</InfoPanel.Label> + <InfoPanel.Text>{rcVersion || '—'}</InfoPanel.Text> + </InfoPanel.Field> + <InfoPanel.Field> <InfoPanel.Label>{t('OS')}</InfoPanel.Label> <InfoPanel.Text>{`${os?.name || ''} ${os?.version || ''}`}</InfoPanel.Text> From 8ac075833579f0e1429763f1d3ae6b1abc383229 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:40:50 -0300 Subject: [PATCH 046/149] fix: call-management permission not doing anything (#29524) --- .changeset/call-management-permission.md | 5 +++++ apps/meteor/app/api/server/v1/videoConference.ts | 4 ++++ apps/meteor/app/authorization/server/constant/permissions.ts | 2 +- apps/meteor/app/videobridge/client/tabBar.tsx | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .changeset/call-management-permission.md diff --git a/.changeset/call-management-permission.md b/.changeset/call-management-permission.md new file mode 100644 index 000000000000..ad18de8de1ce --- /dev/null +++ b/.changeset/call-management-permission.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +fix: Permission to start conference calls was not being considered diff --git a/apps/meteor/app/api/server/v1/videoConference.ts b/apps/meteor/app/api/server/v1/videoConference.ts index bd95433773db..9fb13b7dbdb9 100644 --- a/apps/meteor/app/api/server/v1/videoConference.ts +++ b/apps/meteor/app/api/server/v1/videoConference.ts @@ -26,6 +26,10 @@ API.v1.addRoute( return API.v1.failure('invalid-params'); } + if (!(await hasPermissionAsync(userId, 'call-management', roomId))) { + return API.v1.unauthorized(); + } + try { const providerName = videoConfProviders.getActiveProvider(); diff --git a/apps/meteor/app/authorization/server/constant/permissions.ts b/apps/meteor/app/authorization/server/constant/permissions.ts index 053f39c3af0b..0090d10fd880 100644 --- a/apps/meteor/app/authorization/server/constant/permissions.ts +++ b/apps/meteor/app/authorization/server/constant/permissions.ts @@ -83,7 +83,7 @@ export const permissions = [ { _id: 'preview-c-room', roles: ['admin', 'user', 'anonymous'] }, { _id: 'view-outside-room', roles: ['admin', 'owner', 'moderator', 'user'] }, { _id: 'view-broadcast-member-list', roles: ['admin', 'owner', 'moderator'] }, - { _id: 'call-management', roles: ['admin', 'owner', 'moderator'] }, + { _id: 'call-management', roles: ['admin', 'owner', 'moderator', 'user'] }, { _id: 'create-invite-links', roles: ['admin', 'owner', 'moderator'] }, { _id: 'view-l-room', diff --git a/apps/meteor/app/videobridge/client/tabBar.tsx b/apps/meteor/app/videobridge/client/tabBar.tsx index 80b430594942..23fb71b09e35 100644 --- a/apps/meteor/app/videobridge/client/tabBar.tsx +++ b/apps/meteor/app/videobridge/client/tabBar.tsx @@ -44,6 +44,7 @@ addAction('start-call', ({ room }) => { const isRinging = useVideoConfIsRinging(); const federated = isRoomFederated(room); const canPostReadOnly = usePermission('post-readonly', room._id); + const canStartCall = usePermission('call-management', room._id); const ownUser = room.uids && room.uids.length === 1; @@ -57,7 +58,7 @@ addAction('start-call', ({ room }) => { const live = room?.streamingOptions && room.streamingOptions.type === 'call'; const enabled = enabledDMs || enabledChannel || enabledTeams || enabledGroups || enabledLiveChat; - const enableOption = enabled && (!user?.username || !room.muted?.includes(user.username)); + const enableOption = enabled && canStartCall && (!user?.username || !room.muted?.includes(user.username)); const groups = useStableArray( [ From 0856e8519015be36a6338d975b69df4ee89e3ffe Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Mon, 3 Jul 2023 14:31:16 -0300 Subject: [PATCH 047/149] chore: upgrading fuselage package (#29703) --- yarn.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7c98d73a7961..b3fc2efa9391 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9392,7 +9392,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.12, @rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.146": +"@rocket.chat/css-in-js@npm:^0.31.12, @rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.151": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9418,7 +9418,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.146": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.151": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9647,10 +9647,10 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.322": - version: 0.32.0-dev.322 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.322" - checksum: 95a8319aaa4f6fd429c62a594b4094136ec57c519201140ca029bbe44d49cb3fd381fb67d7e20f1341308868794dbc9609dad180c4ab38ddcbd89eea3e78451a +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.327": + version: 0.32.0-dev.327 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.327" + checksum: 93597b59a76829d5074834295c0b13ecf1256433bdedab6ce5f92b50799893ca8f5eceabcdeaba0fbd24df7abd64f4c62fa28f008d8fa90a7ecd869bbd8b0d6a languageName: node linkType: hard @@ -9710,14 +9710,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.372 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.372" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.146 - "@rocket.chat/css-supports": ~0.31.23-dev.146 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.322 - "@rocket.chat/memo": ~0.31.23-dev.146 - "@rocket.chat/styled": ~0.31.23-dev.146 + version: 0.32.0-dev.377 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.377" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.151 + "@rocket.chat/css-supports": ~0.31.23-dev.151 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.327 + "@rocket.chat/memo": ~0.31.23-dev.151 + "@rocket.chat/styled": ~0.31.23-dev.151 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9729,7 +9729,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 47cdb72c37ee36ebc1dd3daeb8d03950b674dbc3637918dc3d8f6aeead7a56a34db6767e3514e62fc0a9d6a811ef0325cc17c8ffc79532b87f720559ef940ce0 + checksum: e61ddd6ce6dd7ea4ba8d5b5c5f464a82d5600934c7ae05a38f20227308f39b55dbd5b91f9190d494b814efac6e60eb70810ab7d171ff1b482e54a2132573bdda languageName: node linkType: hard @@ -9957,7 +9957,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.146": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.151": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -10762,7 +10762,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.146": +"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.151": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: From cb4048a911f12d154dc0bfc68747d5204f155881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Mon, 3 Jul 2023 17:31:31 -0300 Subject: [PATCH 048/149] chore: update button-secondary colors (#29708) --- ee/packages/ui-theming/src/paletteDark.ts | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ee/packages/ui-theming/src/paletteDark.ts b/ee/packages/ui-theming/src/paletteDark.ts index 80a29c07cd2d..6c4a22fa26cc 100644 --- a/ee/packages/ui-theming/src/paletteDark.ts +++ b/ee/packages/ui-theming/src/paletteDark.ts @@ -131,23 +131,23 @@ export const palette = [ { description: 'Secondary Background', list: [ - { name: 'button-background-secondary-default', token: 'N800', color: '#2F343D' }, - { name: 'button-background-secondary-hover', token: '', color: '#3A404B' }, - { name: 'button-background-secondary-press', token: '', color: '#454C59' }, - { name: 'button-background-secondary-focus', token: 'N800', color: '#2F343D' }, - { name: 'button-background-secondary-keyfocus', token: 'N800', color: '#2F343D' }, - { name: 'button-background-secondary-disabled', token: '', color: '#2F343D' }, + { name: 'button-background-secondary-default', token: 'N800', color: '#353B45' }, + { name: 'button-background-secondary-hover', token: '', color: '#404754' }, + { name: 'button-background-secondary-press', token: '', color: '#4C5362' }, + { name: 'button-background-secondary-focus', token: 'N800', color: '#353B45' }, + { name: 'button-background-secondary-keyfocus', token: 'N800', color: '#353B45' }, + { name: 'button-background-secondary-disabled', token: '', color: '#353B45' }, ], }, { description: 'Secondary Danger Background', list: [ - { name: 'button-background-secondary-danger-default', token: 'N800', color: '#2F343D' }, - { name: 'button-background-secondary-danger-hover', token: '', color: '#3A404B' }, - { name: 'button-background-secondary-danger-press', token: '', color: '#454C59' }, - { name: 'button-background-secondary-danger-focus', token: 'N800', color: '#2F343D' }, - { name: 'button-background-secondary-danger-keyfocus', token: 'N800', color: '#2F343D' }, - { name: 'button-background-secondary-danger-disabled', token: '', color: '#2F343D' }, + { name: 'button-background-secondary-danger-default', token: 'N800', color: '#353B45' }, + { name: 'button-background-secondary-danger-hover', token: '', color: '#404754' }, + { name: 'button-background-secondary-danger-press', token: '', color: '#4C5362' }, + { name: 'button-background-secondary-danger-focus', token: 'N800', color: '#353B45' }, + { name: 'button-background-secondary-danger-keyfocus', token: 'N800', color: '#353B45' }, + { name: 'button-background-secondary-danger-disabled', token: '', color: '#353B45' }, ], }, { From ba1d0c5d6381fcef8900a7ce9cbf77c27fe668dc Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Mon, 3 Jul 2023 18:20:44 -0300 Subject: [PATCH 049/149] fix: Video Conf information block not updating automatically (#29682) Co-authored-by: Diego Sampaio <chinello@gmail.com> --- apps/meteor/server/modules/listeners/listeners.module.ts | 7 +++++++ apps/meteor/server/services/video-conference/service.ts | 6 +----- packages/core-services/src/Events.ts | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/meteor/server/modules/listeners/listeners.module.ts b/apps/meteor/server/modules/listeners/listeners.module.ts index a1898592c5af..45d8e75952fb 100644 --- a/apps/meteor/server/modules/listeners/listeners.module.ts +++ b/apps/meteor/server/modules/listeners/listeners.module.ts @@ -132,6 +132,13 @@ export class ListenersModule { }, ); + service.onEvent('room.video-conference', ({ rid, callId }) => { + /* deprecated */ + (notifications.notifyRoom as any)(rid, callId); + + notifications.notifyRoom(rid, 'videoconf', callId); + }); + service.onEvent('presence.status', ({ user }) => { const { _id, username, name, status, statusText, roles } = user; if (!status || !username) { diff --git a/apps/meteor/server/services/video-conference/service.ts b/apps/meteor/server/services/video-conference/service.ts index ed44cee96e5c..2543e91a3e47 100644 --- a/apps/meteor/server/services/video-conference/service.ts +++ b/apps/meteor/server/services/video-conference/service.ts @@ -41,7 +41,6 @@ import { updateCounter } from '../../../app/statistics/server/functions/updateSt import { readSecondaryPreferred } from '../../database/readSecondaryPreferred'; import { availabilityErrors } from '../../../lib/videoConference/constants'; import { callbacks } from '../../../lib/callbacks'; -import { Notifications } from '../../../app/notifications/server'; import { canAccessRoomIdAsync } from '../../../app/authorization/server/functions/canAccessRoom'; import { i18n } from '../../lib/i18n'; @@ -419,10 +418,7 @@ export class VideoConfService extends ServiceClassInternal implements IVideoConf } private notifyVideoConfUpdate(rid: IRoom['_id'], callId: VideoConference['_id']): void { - /* deprecated */ - (Notifications.notifyRoom as any)(rid, callId); - - Notifications.notifyRoom(rid, 'videoconf', callId); + void api.broadcast('room.video-conference', { rid, callId }); } private async endCall(callId: VideoConference['_id']): Promise<void> { diff --git a/packages/core-services/src/Events.ts b/packages/core-services/src/Events.ts index ed04a2404ece..56187608df00 100644 --- a/packages/core-services/src/Events.ts +++ b/packages/core-services/src/Events.ts @@ -36,6 +36,7 @@ import type { AutoUpdateRecord } from './types/IMeteor'; type ClientAction = 'inserted' | 'updated' | 'removed' | 'changed'; export type EventSignatures = { + 'room.video-conference': (params: { rid: string; callId: string }) => void; 'shutdown': (params: Record<string, string[]>) => void; '$services.changed': (info: { localService: boolean }) => void; 'accounts.login': (info: { userId: string; connection: ISocketConnection }) => void; From e05e581a466609caf03150819c74ce84349fc438 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento <rodrigoknascimento@gmail.com> Date: Mon, 3 Jul 2023 18:21:35 -0300 Subject: [PATCH 050/149] chore: Improve performance of getActiveLocalUserCount (#29681) Co-authored-by: Diego Sampaio <chinello@gmail.com> --- apps/meteor/server/models/raw/Users.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index f8ae05e11d11..0cb88a178ccc 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -2815,19 +2815,17 @@ export class UsersRaw extends BaseRaw { // here getActiveLocalUserCount() { return Promise.all([ + // Count all active users (fast based on index) this.col.countDocuments({ active: true, - type: { - $nin: ['app'], - }, - roles: { $ne: ['guest'] }, }), - this.col.countDocuments({ federated: true, active: true }), + // Count all active that are guests, apps or federated + // Fast based on indexes, usually based on guest index as is usually small this.col.countDocuments({ - isRemote: true, active: true, - roles: { $ne: ['guest'] }, + $or: [{ roles: ['guest'] }, { type: 'app' }, { federated: true }, { isRemote: true }], }), + // Get all active and remove the guests, apps, federated, etc ]).then((results) => results.reduce((a, b) => a - b)); } From ca8d94b94faa64d96efd764af789bc083a09867c Mon Sep 17 00:00:00 2001 From: Hugo Costa <hugocarreiracosta@gmail.com> Date: Tue, 4 Jul 2023 10:01:09 -0300 Subject: [PATCH 051/149] ci: fixing and updating Stale action (#29653) --- .github/workflows/stale.yml | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6ffbea0997e4..b074212964eb 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,24 +1,20 @@ -name: Stale Questions - +name: Close inactive issues on: schedule: - - cron: "0 */6 * * *" + - cron: "0 */6 * * *" jobs: - no-response: + close-issues: runs-on: ubuntu-latest + permissions: + issues: write steps: - - uses: actions/stale@v4 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - days-before-stale: 10 - days-before-close: 4 - only-labels: 'stat: need more info,stat: waiting response' - stale-issue-message: >- - This issue has been marked as stale because there has been - no further activity in the last 10 days. If the issue remains - stale for the next 4 days (a total of two weeks with no activity), - then it will be assumed that the question has been resolved and - the issue will be automatically closed. - stale-issue-label: 'stat: no response' - operations-per-run: 40 + - uses: actions/stale@v5 + with: + days-before-issue-stale: 10 + days-before-issue-close: 4 + any-of-labels: 'stat: need more info,stat: waiting response' + stale-issue-label: "stat: no response" + stale-issue-message: "This issue has been marked as stale because there has been no further activity in the last 10 days. If the issue remains stale for the next 4 days (a total of 14 days with no activity), then it will be assumed that the question has been resolved and the issue will be automatically closed." + close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." + repo-token: ${{ secrets.GITHUB_TOKEN }} From 3e2d70087dcbadae97ff5841da7d912f42b79706 Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Tue, 4 Jul 2023 11:07:10 -0300 Subject: [PATCH 052/149] fix: avatar is reset in the UI when username is changed (#29685) --- .changeset/green-icons-smash.md | 5 +++++ .../lib/server/functions/saveUserIdentity.ts | 20 +++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 .changeset/green-icons-smash.md diff --git a/.changeset/green-icons-smash.md b/.changeset/green-icons-smash.md new file mode 100644 index 000000000000..5419c2c3fa5b --- /dev/null +++ b/.changeset/green-icons-smash.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Avatar is reset in the UI when username is changed diff --git a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts index a7c986647852..37ec3a890bd3 100644 --- a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts +++ b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts @@ -49,6 +49,16 @@ export async function saveUserIdentity({ _id, name: rawName, username: rawUserna // if coming from old username, update all references if (previousUsername) { if (usernameChanged && typeof rawUsername !== 'undefined') { + const fileStore = FileUpload.getStore('Avatars'); + const previousFile = await fileStore.model.findOneByName(previousUsername); + const file = await fileStore.model.findOneByName(username); + if (file) { + await fileStore.model.deleteFile(file._id); + } + if (previousFile) { + await fileStore.model.updateFileNameById(previousFile._id, username); + } + await Messages.updateAllUsernamesByUserId(user._id, username); await Messages.updateUsernameOfEditByUserId(user._id, username); @@ -64,16 +74,6 @@ export async function saveUserIdentity({ _id, name: rawName, username: rawUserna await Subscriptions.setUserUsernameByUserId(user._id, username); await LivechatDepartmentAgents.replaceUsernameOfAgentByUserId(user._id, username); - - const fileStore = FileUpload.getStore('Avatars'); - const previousFile = await fileStore.model.findOneByName(previousUsername); - const file = await fileStore.model.findOneByName(username); - if (file) { - await fileStore.model.deleteFile(file._id); - } - if (previousFile) { - await fileStore.model.updateFileNameById(previousFile._id, username); - } } // update other references if either the name or username has changed From c10137e015b2cfa42aa7976d88e512e2ed0ed34e Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Tue, 4 Jul 2023 11:40:48 -0300 Subject: [PATCH 053/149] feat: Outlook Calendar Integration (#27922) --- .vscode/settings.json | 1 + apps/meteor/app/api/server/index.ts | 1 + apps/meteor/app/api/server/v1/calendar.ts | 139 ++++ .../GenericModal.stories.tsx | 0 .../{ => GenericModal}/GenericModal.tsx | 2 +- .../GenericModal/GenericModalSkeleton.tsx | 25 + .../client/components/GenericModal/index.ts | 2 + .../{ => GenericModal}/withDoNotAskAgain.tsx | 2 +- .../lib/utils/window.RocketChatDesktop.d.ts | 14 + apps/meteor/client/main.ts | 1 + .../client/providers/VideoConfProvider.tsx | 4 +- .../notifications/konchatNotifications.ts | 40 +- .../preferences/AccountPreferencesPage.tsx | 1 + .../PreferencesNotificationsSection.tsx | 17 + .../views/conference/ConferencePage.tsx | 4 +- ...erativeModal.ts => useImperativeModal.tsx} | 15 +- .../OutlookCalendarEventModal.tsx | 53 ++ .../OutlookEventsList/OutlookEventItem.tsx | 67 ++ .../OutlookEventItemContent.tsx | 25 + .../OutlookEventsList/OutlookEventsList.tsx | 139 ++++ .../OutlookEventsList/index.ts | 1 + .../outlookCalendar/OutlookEventsRoute.tsx | 25 + .../OutlookSettingItem.tsx | 58 ++ .../OutlookSettingsList.tsx | 87 +++ .../OutlookSettingsList/index.ts | 1 + .../hooks/useOutlookAuthentication.ts | 59 ++ .../hooks/useOutlookCalendarList.ts | 53 ++ .../hooks/useOutlookOpenCall.ts | 18 + .../client/views/outlookCalendar/index.ts | 1 + .../outlookCalendar/lib/NotOnDesktopError.ts | 5 + .../outlookCalendar/lib/syncOutlookEvents.ts | 15 + .../client/views/outlookCalendar/tabBar.ts | 23 + .../hooks/useVideoConfOpenCall.tsx | 18 +- .../ConvertToChannelModal.tsx | 12 +- apps/meteor/ee/app/license/server/bundles.ts | 4 +- apps/meteor/ee/server/configuration/index.ts | 1 + .../server/configuration/outlookCalendar.ts | 13 + .../ee/server/settings/outlookCalendar.ts | 41 ++ .../rocketchat-i18n/i18n/en.i18n.json | 26 + .../server/methods/saveUserPreferences.ts | 2 + apps/meteor/server/models/CalendarEvent.ts | 6 + .../meteor/server/models/raw/CalendarEvent.ts | 124 ++++ apps/meteor/server/models/startup.ts | 1 + .../modules/listeners/listeners.module.ts | 4 + .../server/services/calendar/service.ts | 233 +++++++ apps/meteor/server/services/startup.ts | 2 + apps/meteor/server/settings/accounts.ts | 6 + apps/meteor/tests/data/user.ts | 1 + .../tests/end-to-end/api/00-miscellaneous.js | 1 + .../tests/end-to-end/api/30-calendar.ts | 660 ++++++++++++++++++ packages/core-services/src/Events.ts | 2 + packages/core-services/src/index.ts | 3 + .../src/types/ICalendarService.ts | 15 + packages/core-typings/src/ICalendarEvent.ts | 16 + packages/core-typings/src/INotification.ts | 9 + packages/core-typings/src/index.ts | 1 + packages/cron/src/index.ts | 53 +- packages/model-typings/src/index.ts | 1 + .../src/models/ICalendarEventModel.ts | 16 + packages/models/src/index.ts | 2 + packages/rest-typings/src/index.ts | 4 + .../v1/calendar/CalendarEventCreateProps.ts | 47 ++ .../v1/calendar/CalendarEventDeleteProps.ts | 22 + .../v1/calendar/CalendarEventImportProps.ts | 47 ++ .../src/v1/calendar/CalendarEventInfoProps.ts | 22 + .../src/v1/calendar/CalendarEventListProps.ts | 22 + .../v1/calendar/CalendarEventUpdateProps.ts | 48 ++ .../rest-typings/src/v1/calendar/index.ts | 41 ++ .../v1/users/UsersSetPreferenceParamsPOST.ts | 5 + .../ui-contexts/src/ServerContext/streams.ts | 2 + 70 files changed, 2385 insertions(+), 46 deletions(-) create mode 100644 apps/meteor/app/api/server/v1/calendar.ts rename apps/meteor/client/components/{ => GenericModal}/GenericModal.stories.tsx (100%) rename apps/meteor/client/components/{ => GenericModal}/GenericModal.tsx (98%) create mode 100644 apps/meteor/client/components/GenericModal/GenericModalSkeleton.tsx create mode 100644 apps/meteor/client/components/GenericModal/index.ts rename apps/meteor/client/components/{ => GenericModal}/withDoNotAskAgain.tsx (96%) create mode 100644 apps/meteor/client/lib/utils/window.RocketChatDesktop.d.ts rename apps/meteor/client/views/hooks/{useImperativeModal.ts => useImperativeModal.tsx} (57%) create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookCalendarEventModal.tsx create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItemContent.tsx create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookEventsList/index.ts create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookEventsRoute.tsx create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingItem.tsx create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingsList.tsx create mode 100644 apps/meteor/client/views/outlookCalendar/OutlookSettingsList/index.ts create mode 100644 apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts create mode 100644 apps/meteor/client/views/outlookCalendar/hooks/useOutlookCalendarList.ts create mode 100644 apps/meteor/client/views/outlookCalendar/hooks/useOutlookOpenCall.ts create mode 100644 apps/meteor/client/views/outlookCalendar/index.ts create mode 100644 apps/meteor/client/views/outlookCalendar/lib/NotOnDesktopError.ts create mode 100644 apps/meteor/client/views/outlookCalendar/lib/syncOutlookEvents.ts create mode 100644 apps/meteor/client/views/outlookCalendar/tabBar.ts create mode 100644 apps/meteor/ee/server/configuration/outlookCalendar.ts create mode 100644 apps/meteor/ee/server/settings/outlookCalendar.ts create mode 100644 apps/meteor/server/models/CalendarEvent.ts create mode 100644 apps/meteor/server/models/raw/CalendarEvent.ts create mode 100644 apps/meteor/server/services/calendar/service.ts create mode 100644 apps/meteor/tests/end-to-end/api/30-calendar.ts create mode 100644 packages/core-services/src/types/ICalendarService.ts create mode 100644 packages/core-typings/src/ICalendarEvent.ts create mode 100644 packages/model-typings/src/models/ICalendarEventModel.ts create mode 100644 packages/rest-typings/src/v1/calendar/CalendarEventCreateProps.ts create mode 100644 packages/rest-typings/src/v1/calendar/CalendarEventDeleteProps.ts create mode 100644 packages/rest-typings/src/v1/calendar/CalendarEventImportProps.ts create mode 100644 packages/rest-typings/src/v1/calendar/CalendarEventInfoProps.ts create mode 100644 packages/rest-typings/src/v1/calendar/CalendarEventListProps.ts create mode 100644 packages/rest-typings/src/v1/calendar/CalendarEventUpdateProps.ts create mode 100644 packages/rest-typings/src/v1/calendar/index.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index b2ff9086c4ce..c9d889142050 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,7 @@ "typescript.tsdk": "./node_modules/typescript/lib", "cSpell.words": [ "autotranslate", + "Contextualbar", "fname", "Gazzodown", "katex", diff --git a/apps/meteor/app/api/server/index.ts b/apps/meteor/app/api/server/index.ts index 1cf198eaf9c2..699bfdacb3aa 100644 --- a/apps/meteor/app/api/server/index.ts +++ b/apps/meteor/app/api/server/index.ts @@ -7,6 +7,7 @@ import './helpers/isUserFromParams'; import './helpers/parseJsonQuery'; import './default/info'; import './v1/assets'; +import './v1/calendar'; import './v1/channels'; import './v1/chat'; import './v1/cloud'; diff --git a/apps/meteor/app/api/server/v1/calendar.ts b/apps/meteor/app/api/server/v1/calendar.ts new file mode 100644 index 000000000000..494f5ad03e24 --- /dev/null +++ b/apps/meteor/app/api/server/v1/calendar.ts @@ -0,0 +1,139 @@ +import { + isCalendarEventListProps, + isCalendarEventCreateProps, + isCalendarEventImportProps, + isCalendarEventInfoProps, + isCalendarEventUpdateProps, + isCalendarEventDeleteProps, +} from '@rocket.chat/rest-typings'; +import { Calendar } from '@rocket.chat/core-services'; + +import { API } from '../api'; + +API.v1.addRoute( + 'calendar-events.list', + { authRequired: true, validateParams: isCalendarEventListProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } }, + { + async get() { + const { userId } = this; + const { date } = this.queryParams; + + const data = await Calendar.list(userId, new Date(date)); + + return API.v1.success({ data }); + }, + }, +); + +API.v1.addRoute( + 'calendar-events.info', + { authRequired: true, validateParams: isCalendarEventInfoProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } }, + { + async get() { + const { userId } = this; + const { id } = this.queryParams; + + const event = await Calendar.get(id); + + if (!event || event.uid !== userId) { + return API.v1.failure(); + } + + return API.v1.success({ event }); + }, + }, +); + +API.v1.addRoute( + 'calendar-events.create', + { authRequired: true, validateParams: isCalendarEventCreateProps }, + { + async post() { + const { userId: uid } = this; + const { startTime, externalId, subject, description, meetingUrl, reminderMinutesBeforeStart } = this.bodyParams; + + const id = await Calendar.create({ + uid, + startTime: new Date(startTime), + externalId, + subject, + description, + meetingUrl, + reminderMinutesBeforeStart, + }); + + return API.v1.success({ id }); + }, + }, +); + +API.v1.addRoute( + 'calendar-events.import', + { authRequired: true, validateParams: isCalendarEventImportProps }, + { + async post() { + const { userId: uid } = this; + const { startTime, externalId, subject, description, meetingUrl, reminderMinutesBeforeStart } = this.bodyParams; + + const id = await Calendar.import({ + uid, + startTime: new Date(startTime), + externalId, + subject, + description, + meetingUrl, + reminderMinutesBeforeStart, + }); + + return API.v1.success({ id }); + }, + }, +); + +API.v1.addRoute( + 'calendar-events.update', + { authRequired: true, validateParams: isCalendarEventUpdateProps }, + { + async post() { + const { userId } = this; + const { eventId, startTime, subject, description, meetingUrl, reminderMinutesBeforeStart } = this.bodyParams; + + const event = await Calendar.get(eventId); + + if (!event || event.uid !== userId) { + throw new Error('invalid-calendar-event'); + } + + await Calendar.update(eventId, { + startTime: new Date(startTime), + subject, + description, + meetingUrl, + reminderMinutesBeforeStart, + }); + + return API.v1.success(); + }, + }, +); + +API.v1.addRoute( + 'calendar-events.delete', + { authRequired: true, validateParams: isCalendarEventDeleteProps }, + { + async post() { + const { userId } = this; + const { eventId } = this.bodyParams; + + const event = await Calendar.get(eventId); + + if (!event || event.uid !== userId) { + throw new Error('invalid-calendar-event'); + } + + await Calendar.delete(eventId); + + return API.v1.success(); + }, + }, +); diff --git a/apps/meteor/client/components/GenericModal.stories.tsx b/apps/meteor/client/components/GenericModal/GenericModal.stories.tsx similarity index 100% rename from apps/meteor/client/components/GenericModal.stories.tsx rename to apps/meteor/client/components/GenericModal/GenericModal.stories.tsx diff --git a/apps/meteor/client/components/GenericModal.tsx b/apps/meteor/client/components/GenericModal/GenericModal.tsx similarity index 98% rename from apps/meteor/client/components/GenericModal.tsx rename to apps/meteor/client/components/GenericModal/GenericModal.tsx index 64dd981b0c32..09a9598dafc2 100644 --- a/apps/meteor/client/components/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal/GenericModal.tsx @@ -98,7 +98,7 @@ const GenericModal: FC<GenericModalProps> = ({ {confirmText ?? t('Ok')} </Button> )} - {!wrapperFunction && ( + {!wrapperFunction && onConfirm && ( <Button {...getButtonProps(variant)} onClick={onConfirm} disabled={confirmDisabled}> {confirmText ?? t('Ok')} </Button> diff --git a/apps/meteor/client/components/GenericModal/GenericModalSkeleton.tsx b/apps/meteor/client/components/GenericModal/GenericModalSkeleton.tsx new file mode 100644 index 000000000000..d56cbdd26a67 --- /dev/null +++ b/apps/meteor/client/components/GenericModal/GenericModalSkeleton.tsx @@ -0,0 +1,25 @@ +import { Skeleton } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { ComponentProps } from 'react'; +import React from 'react'; + +import GenericModal from './GenericModal'; + +const GenericModalSkeleton = ({ onClose, ...props }: ComponentProps<typeof GenericModal>) => { + const t = useTranslation(); + + return ( + <GenericModal + {...props} + variant='warning' + onClose={onClose} + title={<Skeleton width='50%' />} + confirmText={t('Cancel')} + onConfirm={onClose} + > + <Skeleton width='full' /> + </GenericModal> + ); +}; + +export default GenericModalSkeleton; diff --git a/apps/meteor/client/components/GenericModal/index.ts b/apps/meteor/client/components/GenericModal/index.ts new file mode 100644 index 000000000000..5ef8367e6779 --- /dev/null +++ b/apps/meteor/client/components/GenericModal/index.ts @@ -0,0 +1,2 @@ +export * from './GenericModal'; +export { default } from './GenericModal'; diff --git a/apps/meteor/client/components/withDoNotAskAgain.tsx b/apps/meteor/client/components/GenericModal/withDoNotAskAgain.tsx similarity index 96% rename from apps/meteor/client/components/withDoNotAskAgain.tsx rename to apps/meteor/client/components/GenericModal/withDoNotAskAgain.tsx index e855b6a3c9fb..6d344a8a9674 100644 --- a/apps/meteor/client/components/withDoNotAskAgain.tsx +++ b/apps/meteor/client/components/GenericModal/withDoNotAskAgain.tsx @@ -3,7 +3,7 @@ import { useUserPreference, useTranslation, useEndpoint } from '@rocket.chat/ui- import type { FC, ReactElement, ComponentType } from 'react'; import React, { useState } from 'react'; -import type { DontAskAgainList } from '../hooks/useDontAskAgain'; +import type { DontAskAgainList } from '../../hooks/useDontAskAgain'; type DoNotAskAgainProps = { onConfirm: (...args: any) => Promise<void> | void; diff --git a/apps/meteor/client/lib/utils/window.RocketChatDesktop.d.ts b/apps/meteor/client/lib/utils/window.RocketChatDesktop.d.ts new file mode 100644 index 000000000000..73f884dc07ce --- /dev/null +++ b/apps/meteor/client/lib/utils/window.RocketChatDesktop.d.ts @@ -0,0 +1,14 @@ +type OutlookEventsResponse = { status: 'success' | 'canceled' }; + +// eslint-disable-next-line @typescript-eslint/naming-convention +interface Window { + RocketChatDesktop: + | { + openInternalVideoChatWindow?: (url: string, options: undefined) => void; + getOutlookEvents?: (date: Date) => Promise<OutlookEventsResponse>; + setOutlookExchangeUrl?: (url: string, userId: string) => Promise<void>; + hasOutlookCredentials?: () => Promise<boolean>; + clearOutlookCredentials?: () => void; + } + | undefined; +} diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index e9dec89db3af..ed98c06c3297 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -13,3 +13,4 @@ import './views/admin'; import './views/marketplace'; import './views/account'; import './views/teams'; +import './views/outlookCalendar'; diff --git a/apps/meteor/client/providers/VideoConfProvider.tsx b/apps/meteor/client/providers/VideoConfProvider.tsx index 9410e12c3ae5..f642ab0f2415 100644 --- a/apps/meteor/client/providers/VideoConfProvider.tsx +++ b/apps/meteor/client/providers/VideoConfProvider.tsx @@ -8,11 +8,11 @@ import { VideoConfContext } from '../contexts/VideoConfContext'; import type { DirectCallParams, ProviderCapabilities, CallPreferences } from '../lib/VideoConfManager'; import { VideoConfManager } from '../lib/VideoConfManager'; import VideoConfPopups from '../views/room/contextualBar/VideoConference/VideoConfPopups'; -import { useVideoOpenCall } from '../views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall'; +import { useVideoConfOpenCall } from '../views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall'; const VideoConfContextProvider = ({ children }: { children: ReactNode }): ReactElement => { const [outgoing, setOutgoing] = useState<VideoConfPopupPayload | undefined>(); - const handleOpenCall = useVideoOpenCall(); + const handleOpenCall = useVideoConfOpenCall(); useEffect( () => diff --git a/apps/meteor/client/startup/notifications/konchatNotifications.ts b/apps/meteor/client/startup/notifications/konchatNotifications.ts index 20b6ba058059..8729ee3da271 100644 --- a/apps/meteor/client/startup/notifications/konchatNotifications.ts +++ b/apps/meteor/client/startup/notifications/konchatNotifications.ts @@ -1,17 +1,22 @@ -import type { AtLeast, ISubscription, IUser } from '@rocket.chat/core-typings'; +import type { AtLeast, ISubscription, IUser, ICalendarNotification } from '@rocket.chat/core-typings'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; +import { lazy } from 'react'; import { CachedChatSubscription } from '../../../app/models/client'; import { Notifications } from '../../../app/notifications/client'; +import { settings } from '../../../app/settings/client'; import { readMessage } from '../../../app/ui-utils/client'; import { KonchatNotification } from '../../../app/ui/client/lib/KonchatNotification'; import { getUserPreference } from '../../../app/utils/client'; import { RoomManager } from '../../lib/RoomManager'; +import { imperativeModal } from '../../lib/imperativeModal'; import { fireGlobalEvent } from '../../lib/utils/fireGlobalEvent'; import { isLayoutEmbedded } from '../../lib/utils/isLayoutEmbedded'; +const OutlookCalendarEventModal = lazy(() => import('../../views/outlookCalendar/OutlookCalendarEventModal')); + const notifyNewRoom = async (sub: AtLeast<ISubscription, 'rid'>): Promise<void> => { const user = Meteor.user() as IUser | null; if (!user || user.status === 'busy') { @@ -41,11 +46,42 @@ function notifyNewMessageAudio(rid?: string): void { } Meteor.startup(() => { + const notifyUserCalendar = async function (notification: ICalendarNotification): Promise<void> { + const user = Meteor.user() as IUser | null; + if (!user || user.status === 'busy') { + return; + } + + const requireInteraction = getUserPreference<boolean>(Meteor.userId(), 'desktopNotificationRequireInteraction'); + + const n = new Notification(notification.title, { + body: notification.text, + tag: notification.payload._id, + silent: true, + requireInteraction, + } as NotificationOptions); + + n.onclick = function () { + this.close(); + window.focus(); + imperativeModal.open({ + component: OutlookCalendarEventModal, + props: { id: notification.payload._id, onClose: imperativeModal.close, onCancel: imperativeModal.close }, + }); + }; + }; + Tracker.autorun(() => { + if (!Meteor.userId() || !settings.get('Outlook_Calendar_Enabled')) { + return Notifications.unUser('calendar'); + } + + Notifications.onUser('calendar', notifyUserCalendar); + }); + Tracker.autorun(() => { if (!Meteor.userId()) { return; } - Notifications.onUser('notification', (notification) => { const openedRoomId = ['channel', 'group', 'direct'].includes(FlowRouter.getRouteName()) ? RoomManager.opened : undefined; diff --git a/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx b/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx index 115b32ebc57b..9176dc773285 100644 --- a/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx +++ b/apps/meteor/client/views/account/preferences/AccountPreferencesPage.tsx @@ -46,6 +46,7 @@ type CurrentData = { muteFocusedConversations: boolean; receiveLoginDetectionEmail: boolean; dontAskAgainList: [action: string, label: string][]; + notifyCalendarEvents: boolean; }; export type FormSectionProps = { diff --git a/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx b/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx index fc4c174b90b2..6801794169dd 100644 --- a/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx +++ b/apps/meteor/client/views/account/preferences/PreferencesNotificationsSection.tsx @@ -30,6 +30,7 @@ const PreferencesNotificationsSection = ({ onChange, commitRef, ...props }: Form const userMobileNotifications = useUserPreference('pushNotifications'); const userEmailNotificationMode = useUserPreference('emailNotificationMode') as keyof typeof emailNotificationOptionsLabelMap; const userReceiveLoginDetectionEmail = useUserPreference('receiveLoginDetectionEmail'); + const userNotifyCalendarEvents = useUserPreference('notifyCalendarEvents'); const defaultDesktopNotifications = useSetting( 'Accounts_Default_User_Preferences_desktopNotifications', @@ -42,6 +43,7 @@ const PreferencesNotificationsSection = ({ onChange, commitRef, ...props }: Form const loginEmailEnabled = useSetting('Device_Management_Enable_Login_Emails'); const allowLoginEmailPreference = useSetting('Device_Management_Allow_Login_Email_preference'); const showNewLoginEmailPreference = loginEmailEnabled && allowLoginEmailPreference; + const showCalendarPreference = useSetting('Outlook_Calendar_Enabled'); const { values, handlers, commit } = useForm( { @@ -50,6 +52,7 @@ const PreferencesNotificationsSection = ({ onChange, commitRef, ...props }: Form pushNotifications: userMobileNotifications, emailNotificationMode: userEmailNotificationMode, receiveLoginDetectionEmail: userReceiveLoginDetectionEmail, + notifyCalendarEvents: userNotifyCalendarEvents, }, onChange, ); @@ -60,12 +63,14 @@ const PreferencesNotificationsSection = ({ onChange, commitRef, ...props }: Form pushNotifications, emailNotificationMode, receiveLoginDetectionEmail, + notifyCalendarEvents, } = values as { desktopNotificationRequireInteraction: boolean; desktopNotifications: string; pushNotifications: string; emailNotificationMode: string; receiveLoginDetectionEmail: boolean; + notifyCalendarEvents: boolean; }; const { @@ -74,6 +79,7 @@ const PreferencesNotificationsSection = ({ onChange, commitRef, ...props }: Form handlePushNotifications, handleEmailNotificationMode, handleReceiveLoginDetectionEmail, + handleNotifyCalendarEvents, } = handlers; useEffect(() => setNotificationsPermission(window.Notification && Notification.permission), []); @@ -186,6 +192,17 @@ const PreferencesNotificationsSection = ({ onChange, commitRef, ...props }: Form <Field.Hint>{t('Receive_Login_Detection_Emails_Description')}</Field.Hint> </Field> )} + + {showCalendarPreference && ( + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('Notify_Calendar_Events')}</Field.Label> + <Field.Row> + <ToggleSwitch checked={notifyCalendarEvents} onChange={handleNotifyCalendarEvents} /> + </Field.Row> + </Box> + </Field> + )} </FieldGroup> </Accordion.Item> ); diff --git a/apps/meteor/client/views/conference/ConferencePage.tsx b/apps/meteor/client/views/conference/ConferencePage.tsx index bfc648f04367..b95106efca39 100644 --- a/apps/meteor/client/views/conference/ConferencePage.tsx +++ b/apps/meteor/client/views/conference/ConferencePage.tsx @@ -3,7 +3,7 @@ import type { ReactElement } from 'react'; import React, { useEffect } from 'react'; import { useUserDisplayName } from '../../hooks/useUserDisplayName'; -import { useVideoOpenCall } from '../room/contextualBar/VideoConference/hooks/useVideoConfOpenCall'; +import { useVideoConfOpenCall } from '../room/contextualBar/VideoConference/hooks/useVideoConfOpenCall'; import PageLoading from '../root/PageLoading'; import ConferencePageError from './ConferencePageError'; @@ -19,7 +19,7 @@ const ConferencePage = (): ReactElement => { const user = useUser(); const defaultRoute = useRoute('/'); const setModal = useSetModal(); - const handleOpenCall = useVideoOpenCall(); + const handleOpenCall = useVideoConfOpenCall(); const userDisplayName = useUserDisplayName({ name: user?.name, username: user?.username }); const { callUrlParam } = getQueryParams(); diff --git a/apps/meteor/client/views/hooks/useImperativeModal.ts b/apps/meteor/client/views/hooks/useImperativeModal.tsx similarity index 57% rename from apps/meteor/client/views/hooks/useImperativeModal.ts rename to apps/meteor/client/views/hooks/useImperativeModal.tsx index 2cc193f2cf15..b2bcf233d45b 100644 --- a/apps/meteor/client/views/hooks/useImperativeModal.ts +++ b/apps/meteor/client/views/hooks/useImperativeModal.tsx @@ -1,23 +1,24 @@ import type { Dispatch, SetStateAction, ReactNode } from 'react'; -import { createElement, useEffect } from 'react'; +import React, { Suspense, createElement, useEffect } from 'react'; import { imperativeModal } from '../../lib/imperativeModal'; export const useImperativeModal = (setModal: Dispatch<SetStateAction<ReactNode>>): void => { useEffect(() => { - const unsub = imperativeModal.on('update', (descriptor) => { + return imperativeModal.on('update', (descriptor) => { if (descriptor === null) { return setModal(null); } if ('component' in descriptor) { setModal( - createElement(descriptor.component, { - key: Math.random(), - ...descriptor.props, - }), + <Suspense fallback={<div />}> + {createElement(descriptor.component, { + key: Math.random(), + ...descriptor.props, + })} + </Suspense>, ); } }); - return unsub; }, [setModal]); }; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookCalendarEventModal.tsx b/apps/meteor/client/views/outlookCalendar/OutlookCalendarEventModal.tsx new file mode 100644 index 000000000000..980382717607 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookCalendarEventModal.tsx @@ -0,0 +1,53 @@ +import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; +import type { ComponentProps } from 'react'; +import React from 'react'; + +import GenericModal from '../../components/GenericModal'; +import GenericModalSkeleton from '../../components/GenericModal/GenericModalSkeleton'; +import OutlookEventItemContent from './OutlookEventsList/OutlookEventItemContent'; +import { useOutlookOpenCall } from './hooks/useOutlookOpenCall'; + +type OutlookCalendarEventModalProps = ComponentProps<typeof GenericModal> & { + id?: string; + subject?: string; + meetingUrl?: string; + description?: string; +}; + +const OutlookCalendarEventModal = ({ id, subject, meetingUrl, description, ...props }: OutlookCalendarEventModalProps) => { + const t = useTranslation(); + const calendarInfoEndpoint = useEndpoint('GET', '/v1/calendar-events.info'); + + const { data, isLoading } = useQuery(['calendar-events.info', id], async () => { + if (!id) { + const event = { event: { subject, meetingUrl, description } }; + return event; + } + + return calendarInfoEndpoint({ id }); + }); + + const openCall = useOutlookOpenCall(data?.event.meetingUrl); + + if (isLoading) { + return <GenericModalSkeleton {...props} />; + } + + return ( + <GenericModal + {...props} + tagline={t('Outlook_calendar_event')} + icon={null} + variant='warning' + title={data?.event.subject} + cancelText={t('Close')} + confirmText={t('Join_call')} + onConfirm={openCall} + > + {data?.event.description ? <OutlookEventItemContent html={data?.event.description} /> : t('No_content_was_provided')} + </GenericModal> + ); +}; + +export default OutlookCalendarEventModal; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx new file mode 100644 index 000000000000..f39ef14bf3f3 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItem.tsx @@ -0,0 +1,67 @@ +import type { ICalendarEvent, Serialized } from '@rocket.chat/core-typings'; +import { css } from '@rocket.chat/css-in-js'; +import { Box, Button, Palette } from '@rocket.chat/fuselage'; +import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime'; +import OutlookCalendarEventModal from '../OutlookCalendarEventModal'; +import { useOutlookOpenCall } from '../hooks/useOutlookOpenCall'; + +const OutlookEventItem = ({ subject, description, startTime, meetingUrl }: Serialized<ICalendarEvent>) => { + const t = useTranslation(); + const setModal = useSetModal(); + const formatDateAndTime = useFormatDateAndTime(); + const openCall = useOutlookOpenCall(meetingUrl); + + const hovered = css` + &:hover { + cursor: pointer; + } + + &:hover, + &:focus { + background: ${Palette.surface['surface-hover']}; + } + `; + + const handleOpenEvent = () => { + setModal( + <OutlookCalendarEventModal + onClose={() => setModal(null)} + onCancel={() => setModal(null)} + subject={subject} + meetingUrl={meetingUrl} + description={description} + />, + ); + }; + + return ( + <Box + className={hovered} + borderBlockEndWidth={1} + borderBlockEndColor='stroke-extra-light' + borderBlockEndStyle='solid' + pi='x24' + pb='x16' + display='flex' + justifyContent='space-between' + onClick={handleOpenEvent} + > + <Box> + <Box fontScale='h4'>{subject}</Box> + <Box fontScale='c1'>{formatDateAndTime(startTime)}</Box> + </Box> + <Box> + {meetingUrl && ( + <Button onClick={openCall} small> + {t('Join')} + </Button> + )} + </Box> + </Box> + ); +}; + +export default OutlookEventItem; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItemContent.tsx b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItemContent.tsx new file mode 100644 index 000000000000..33bef064d7b7 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventItemContent.tsx @@ -0,0 +1,25 @@ +import { Box } from '@rocket.chat/fuselage'; +import DOMPurify from 'dompurify'; +import React from 'react'; + +type SanitizeProps = { + html: string; + options?: { + [key: string]: string; + }; +}; + +const OutlookEventItemContent = ({ html, options }: SanitizeProps) => { + const defaultOptions = { + ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'br'], + ALLOWED_ATTR: ['href'], + }; + + const sanitize = (dirtyHTML: SanitizeProps['html'], options: SanitizeProps['options']) => ({ + __html: DOMPurify.sanitize(dirtyHTML, { ...defaultOptions, ...options }).toString(), + }); + + return <Box wordBreak='break-word' color='default' dangerouslySetInnerHTML={sanitize(html, options)} />; +}; + +export default OutlookEventItemContent; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx new file mode 100644 index 000000000000..d6788f0020f8 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx @@ -0,0 +1,139 @@ +import { Box, States, StatesIcon, StatesTitle, StatesSubtitle, ButtonGroup, Button, Icon } from '@rocket.chat/fuselage'; +import { useResizeObserver } from '@rocket.chat/fuselage-hooks'; +import { useTranslation, useSetting } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import React from 'react'; +import { Virtuoso } from 'react-virtuoso'; + +import { + ContextualbarHeader, + ContextualbarIcon, + ContextualbarTitle, + ContextualbarClose, + ContextualbarContent, + ContextualbarFooter, + ContextualbarSkeleton, +} from '../../../components/Contextualbar'; +import ScrollableContentWrapper from '../../../components/ScrollableContentWrapper'; +import { getErrorMessage } from '../../../lib/errorHandling'; +import { useOutlookAuthentication } from '../hooks/useOutlookAuthentication'; +import { useMutationOutlookCalendarSync, useOutlookCalendarListForToday } from '../hooks/useOutlookCalendarList'; +import { NotOnDesktopError } from '../lib/NotOnDesktopError'; +import OutlookEventItem from './OutlookEventItem'; + +type OutlookEventsListProps = { + onClose: () => void; + changeRoute: () => void; +}; + +const OutlookEventsList = ({ onClose, changeRoute }: OutlookEventsListProps): ReactElement => { + const t = useTranslation(); + const outlookUrl = useSetting<string>('Outlook_Calendar_Outlook_Url'); + const { authEnabled, isError, error } = useOutlookAuthentication(); + + const hasOutlookMethods = !(isError && error instanceof NotOnDesktopError); + + const syncOutlookCalendar = useMutationOutlookCalendarSync(); + + const calendarListResult = useOutlookCalendarListForToday(); + + const { ref, contentBoxSize: { inlineSize = 378, blockSize = 1 } = {} } = useResizeObserver<HTMLElement>({ + debounceDelay: 200, + }); + + if (calendarListResult.isLoading) { + return <ContextualbarSkeleton />; + } + + const calendarEvents = calendarListResult.data; + const total = calendarEvents?.length || 0; + + return ( + <> + <ContextualbarHeader> + <ContextualbarIcon name='calendar' /> + <ContextualbarTitle>{t('Outlook_calendar')}</ContextualbarTitle> + <ContextualbarClose onClick={onClose} /> + </ContextualbarHeader> + + {hasOutlookMethods && !authEnabled && total === 0 && ( + <> + <ContextualbarContent paddingInline={0} ref={ref} color='default'> + <Box display='flex' flexDirection='column' justifyContent='center' height='100%'> + <States> + <StatesIcon name='user' /> + <StatesTitle>{t('Log_in_to_sync')}</StatesTitle> + </States> + </Box> + </ContextualbarContent> + <ContextualbarFooter> + <ButtonGroup mbs='x8' stretch> + <Button primary disabled={syncOutlookCalendar.isLoading} onClick={() => syncOutlookCalendar.mutate()}> + {syncOutlookCalendar.isLoading ? t('Please_wait') : t('Login')} + </Button> + </ButtonGroup> + </ContextualbarFooter> + </> + )} + + {(authEnabled || !hasOutlookMethods) && ( + <> + <ContextualbarContent paddingInline={0} ref={ref} color='default'> + {(total === 0 || calendarListResult.isError) && ( + <Box display='flex' flexDirection='column' justifyContent='center' height='100%'> + {calendarListResult.isError && ( + <States> + <StatesIcon name='circle-exclamation' variation='danger' /> + <StatesTitle>{t('Something_went_wrong')}</StatesTitle> + <StatesSubtitle>{getErrorMessage(calendarListResult.error)}</StatesSubtitle> + </States> + )} + {!calendarListResult.isError && total === 0 && ( + <States> + <StatesIcon name='calendar' /> + <StatesTitle>{t('No_history')}</StatesTitle> + </States> + )} + </Box> + )} + {calendarListResult.isSuccess && calendarListResult.data.length > 0 && ( + <Box flexGrow={1} flexShrink={1} overflow='hidden' display='flex'> + <Virtuoso + style={{ + height: blockSize, + width: inlineSize, + }} + totalCount={total} + overscan={25} + data={calendarEvents} + components={{ Scroller: ScrollableContentWrapper }} + itemContent={(_index, calendarData): ReactElement => <OutlookEventItem {...calendarData} />} + /> + </Box> + )} + </ContextualbarContent> + <ContextualbarFooter> + <ButtonGroup stretch> + {authEnabled && <Button onClick={changeRoute}>{t('Calendar_settings')}</Button>} + {outlookUrl && ( + <Button onClick={() => window.open(outlookUrl, '_blank')}> + <Icon mie='x4' name='new-window' /> + <Box is='span'>{t('Open_Outlook')}</Box> + </Button> + )} + </ButtonGroup> + {hasOutlookMethods && ( + <ButtonGroup mbs='x8' stretch> + <Button primary disabled={syncOutlookCalendar.isLoading} onClick={() => syncOutlookCalendar.mutate()}> + {syncOutlookCalendar.isLoading ? t('Sync_in_progress') : t('Sync')} + </Button> + </ButtonGroup> + )} + </ContextualbarFooter> + </> + )} + </> + ); +}; + +export default OutlookEventsList; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/index.ts b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/index.ts new file mode 100644 index 000000000000..5f926dd78ce0 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/index.ts @@ -0,0 +1 @@ +export { default } from './OutlookEventsList'; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsRoute.tsx b/apps/meteor/client/views/outlookCalendar/OutlookEventsRoute.tsx new file mode 100644 index 000000000000..ce73e326f02d --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsRoute.tsx @@ -0,0 +1,25 @@ +import React, { useState } from 'react'; + +import { useTabBarClose } from '../room/contexts/ToolboxContext'; +import OutlookEventsList from './OutlookEventsList'; +import OutlookSettingsList from './OutlookSettingsList'; + +type OutlookCalendarRoutes = 'list' | 'settings'; + +const CALENDAR_ROUTES: { [key: string]: OutlookCalendarRoutes } = { + LIST: 'list', + SETTINGS: 'settings', +}; + +const OutlookEventsRoute = () => { + const closeTabBar = useTabBarClose(); + const [calendarRoute, setCalendarRoute] = useState<OutlookCalendarRoutes>('list'); + + if (calendarRoute === CALENDAR_ROUTES.SETTINGS) { + return <OutlookSettingsList onClose={closeTabBar} changeRoute={() => setCalendarRoute(CALENDAR_ROUTES.LIST)} />; + } + + return <OutlookEventsList onClose={closeTabBar} changeRoute={() => setCalendarRoute(CALENDAR_ROUTES.SETTINGS)} />; +}; + +export default OutlookEventsRoute; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingItem.tsx b/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingItem.tsx new file mode 100644 index 000000000000..f2b01622c5df --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingItem.tsx @@ -0,0 +1,58 @@ +import { css } from '@rocket.chat/css-in-js'; +import { Box, Button, Palette } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +type OutlookSettingItemProps = { + id: string; + title: string; + subTitle: string; + enabled: boolean; + handleEnable: (value: boolean) => void; +}; + +const OutlookSettingItem = ({ id, title, subTitle, enabled, handleEnable }: OutlookSettingItemProps) => { + const t = useTranslation(); + + const hovered = css` + &:hover, + &:focus { + background: ${Palette.surface['surface-hover']}; + .rcx-message { + background: ${Palette.surface['surface-hover']}; + } + } + `; + + return ( + <Box + borderBlockEndWidth={1} + borderBlockEndColor='stroke-extra-light' + borderBlockEndStyle='solid' + className={hovered} + pi='x24' + pb='x16' + display='flex' + justifyContent='space-between' + > + <Box mie='x8'> + <Box fontScale='h4'>{title}</Box> + <Box fontScale='p2'>{subTitle}</Box> + </Box> + <Box> + {id === 'authentication' && ( + <Button small onClick={() => handleEnable(!enabled)}> + {t('Disable')} + </Button> + )} + {id !== 'authentication' && ( + <Button primary={!enabled} small onClick={() => handleEnable(!enabled)}> + {enabled ? t('Disable') : t('Enable')} + </Button> + )} + </Box> + </Box> + ); +}; + +export default OutlookSettingItem; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingsList.tsx b/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingsList.tsx new file mode 100644 index 000000000000..3ceb891d7f9d --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/OutlookSettingsList.tsx @@ -0,0 +1,87 @@ +import { ButtonGroup, Button } from '@rocket.chat/fuselage'; +import { useTranslation, useUserPreference, useEndpoint, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import React, { useCallback } from 'react'; + +import { + ContextualbarHeader, + ContextualbarIcon, + ContextualbarTitle, + ContextualbarClose, + ContextualbarContent, + ContextualbarFooter, +} from '../../../components/Contextualbar'; +import { useOutlookAuthentication, useOutlookAuthenticationMutationLogout } from '../hooks/useOutlookAuthentication'; +import OutlookSettingItem from './OutlookSettingItem'; + +type OutlookSettingsListProps = { + onClose: () => void; + changeRoute: () => void; +}; + +const OutlookSettingsList = ({ onClose, changeRoute }: OutlookSettingsListProps): ReactElement => { + const t = useTranslation(); + const dispatchToastMessage = useToastMessageDispatch(); + const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); + const notifyCalendarEvents = useUserPreference('notifyCalendarEvents') as boolean; + const { authEnabled } = useOutlookAuthentication(); + const handleDisableAuth = useOutlookAuthenticationMutationLogout(); + + const handleNotifyCalendarEvents = useCallback( + (value: boolean) => { + try { + saveUserPreferences({ data: { notifyCalendarEvents: value } }); + dispatchToastMessage({ type: 'success', message: t('Preferences_saved') }); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } + }, + [saveUserPreferences, dispatchToastMessage, t], + ); + + const calendarSettings = [ + { + id: 'notification', + title: t('Event_notifications'), + subTitle: t('Event_notifications_description'), + enabled: notifyCalendarEvents, + handleEnable: handleNotifyCalendarEvents, + }, + { + id: 'authentication', + title: t('Outlook_authentication'), + subTitle: t('Outlook_authentication_description'), + enabled: authEnabled, + handleEnable: () => + handleDisableAuth.mutate(undefined, { + onSuccess: changeRoute, + }), + }, + ]; + + return ( + <> + <ContextualbarHeader> + <ContextualbarIcon name='calendar' /> + <ContextualbarTitle>{t('Outlook_calendar_settings')}</ContextualbarTitle> + <ContextualbarClose onClick={onClose} /> + </ContextualbarHeader> + <ContextualbarContent paddingInline={0} color='default'> + {calendarSettings.map((setting, index) => { + if (setting.id === 'authentication' && !setting.enabled) { + return; + } + + return <OutlookSettingItem key={index} {...setting} />; + })} + </ContextualbarContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button onClick={changeRoute}>{t('Back_to_calendar')}</Button> + </ButtonGroup> + </ContextualbarFooter> + </> + ); +}; + +export default OutlookSettingsList; diff --git a/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/index.ts b/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/index.ts new file mode 100644 index 000000000000..dc0d56237ce9 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/OutlookSettingsList/index.ts @@ -0,0 +1 @@ +export { default } from './OutlookSettingsList'; diff --git a/apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts new file mode 100644 index 000000000000..d671c051f8dd --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookAuthentication.ts @@ -0,0 +1,59 @@ +import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +import { NotOnDesktopError } from '../lib/NotOnDesktopError'; + +export const useOutlookAuthentication = () => { + const { + data: authEnabled, + isError, + error, + } = useQuery( + ['outlook', 'auth'], + async () => { + const desktopApp = window.RocketChatDesktop; + if (!desktopApp?.hasOutlookCredentials) { + throw new NotOnDesktopError(); + } + + return Boolean(await desktopApp?.hasOutlookCredentials?.()) || false; + }, + { + onError: (error) => { + console.error(error); + }, + }, + ); + + return { authEnabled: Boolean(authEnabled), isError, error }; +}; + +export const useOutlookAuthenticationMutation = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async () => { + await queryClient.invalidateQueries(['outlook', 'auth']); + }, + }); +}; + +export const useOutlookAuthenticationMutationLogout = () => { + const t = useTranslation(); + const dispatchToastMessage = useToastMessageDispatch(); + const mutation = useOutlookAuthenticationMutation(); + return useMutation({ + mutationFn: async () => { + const desktopApp = window.RocketChatDesktop; + if (!desktopApp?.clearOutlookCredentials) { + throw new NotOnDesktopError(); + } + + await desktopApp.clearOutlookCredentials(); + + return mutation.mutateAsync(); + }, + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('Outlook_authentication_disabled') }); + }, + }); +}; diff --git a/apps/meteor/client/views/outlookCalendar/hooks/useOutlookCalendarList.ts b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookCalendarList.ts new file mode 100644 index 000000000000..0b2c6370d9fa --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookCalendarList.ts @@ -0,0 +1,53 @@ +import { useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +import { syncOutlookEvents } from '../lib/syncOutlookEvents'; +import { useOutlookAuthenticationMutation } from './useOutlookAuthentication'; + +export const useOutlookCalendarListForToday = () => { + return useOutlookCalendarList(new Date()); +}; + +export const useOutlookCalendarList = (date: Date) => { + const calendarData = useEndpoint('GET', '/v1/calendar-events.list'); + + return useQuery( + ['outlook', 'calendar', 'list'], + async () => { + const { data } = await calendarData({ date: date.toISOString() }); + return data; + }, + { + refetchOnWindowFocus: false, + }, + ); +}; + +export const useMutationOutlookCalendarSync = () => { + const t = useTranslation(); + const queryClient = useQueryClient(); + + const checkOutlookCredentials = useOutlookAuthenticationMutation(); + + const dispatchToastMessage = useToastMessageDispatch(); + + const syncMutation = useMutation({ + mutationFn: async () => { + await syncOutlookEvents(); + + await queryClient.invalidateQueries(['outlook', 'calendar', 'list']); + + await checkOutlookCredentials.mutateAsync(); + }, + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('Outlook_Sync_Success') }); + }, + onError: (error) => { + if (error instanceof Error && error.message === 'abort') { + return; + } + dispatchToastMessage({ type: 'error', message: t('Outlook_Sync_Failed') }); + }, + }); + return syncMutation; +}; diff --git a/apps/meteor/client/views/outlookCalendar/hooks/useOutlookOpenCall.ts b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookOpenCall.ts new file mode 100644 index 000000000000..80269ec04b5d --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/hooks/useOutlookOpenCall.ts @@ -0,0 +1,18 @@ +import { useUser } from '@rocket.chat/ui-contexts'; + +import { useUserDisplayName } from '../../../hooks/useUserDisplayName'; +import { useVideoConfOpenCall } from '../../room/contextualBar/VideoConference/hooks/useVideoConfOpenCall'; + +export const useOutlookOpenCall = (meetingUrl?: string) => { + const user = useUser(); + const handleOpenCall = useVideoConfOpenCall(); + const userDisplayName = useUserDisplayName({ name: user?.name, username: user?.username }); + + const namedMeetingUrl = `${meetingUrl}&name=${userDisplayName}`; + + if (!meetingUrl) { + return; + } + + return () => handleOpenCall(namedMeetingUrl); +}; diff --git a/apps/meteor/client/views/outlookCalendar/index.ts b/apps/meteor/client/views/outlookCalendar/index.ts new file mode 100644 index 000000000000..cd95da0a7d92 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/index.ts @@ -0,0 +1 @@ +import './tabBar'; diff --git a/apps/meteor/client/views/outlookCalendar/lib/NotOnDesktopError.ts b/apps/meteor/client/views/outlookCalendar/lib/NotOnDesktopError.ts new file mode 100644 index 000000000000..83a2114657c7 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/lib/NotOnDesktopError.ts @@ -0,0 +1,5 @@ +export class NotOnDesktopError extends Error { + constructor() { + super('Not on desktop'); + } +} diff --git a/apps/meteor/client/views/outlookCalendar/lib/syncOutlookEvents.ts b/apps/meteor/client/views/outlookCalendar/lib/syncOutlookEvents.ts new file mode 100644 index 000000000000..dc2d55696eb2 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/lib/syncOutlookEvents.ts @@ -0,0 +1,15 @@ +import { NotOnDesktopError } from './NotOnDesktopError'; + +export const syncOutlookEvents = async () => { + const date = new Date(); + const desktopApp = window.RocketChatDesktop; + + if (!desktopApp?.getOutlookEvents) { + throw new NotOnDesktopError(); + } + + const response = await desktopApp.getOutlookEvents(date); + if (response.status === 'canceled') { + throw new Error('abort'); + } +}; diff --git a/apps/meteor/client/views/outlookCalendar/tabBar.ts b/apps/meteor/client/views/outlookCalendar/tabBar.ts new file mode 100644 index 000000000000..89943c8c08c7 --- /dev/null +++ b/apps/meteor/client/views/outlookCalendar/tabBar.ts @@ -0,0 +1,23 @@ +import { useSetting } from '@rocket.chat/ui-contexts'; +import { lazy, useMemo } from 'react'; + +import { addAction } from '../room/lib/Toolbox'; + +addAction('outlookCalendar', () => { + const outlookCalendarEnabled = useSetting('Outlook_Calendar_Enabled'); + + return useMemo( + () => + outlookCalendarEnabled + ? { + groups: ['channel', 'group', 'team'], + id: 'outlookCalendar', + icon: 'calendar', + title: 'Outlook_calendar', + template: lazy(() => import('./OutlookEventsRoute')), + order: 999, + } + : null, + [outlookCalendarEnabled], + ); +}); diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.tsx index 3f282ca80067..48093f9bcfd5 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall.tsx @@ -3,21 +3,14 @@ import React, { useCallback } from 'react'; import VideoConfBlockModal from '../VideoConfBlockModal'; -type WindowMaybeDesktop = typeof window & { - RocketChatDesktop?: { - openInternalVideoChatWindow?: (url: string, options: undefined) => void; - }; -}; - -export const useVideoOpenCall = () => { +export const useVideoConfOpenCall = () => { const setModal = useSetModal(); const handleOpenCall = useCallback( (callUrl: string) => { - const windowMaybeDesktop = window as WindowMaybeDesktop; - if (windowMaybeDesktop.RocketChatDesktop?.openInternalVideoChatWindow) { - windowMaybeDesktop.RocketChatDesktop.openInternalVideoChatWindow(callUrl, undefined); - } else { + const desktopApp = window.RocketChatDesktop; + + if (!desktopApp?.openInternalVideoChatWindow) { const open = () => window.open(callUrl); const popup = open(); @@ -26,7 +19,10 @@ export const useVideoOpenCall = () => { } setModal(<VideoConfBlockModal onClose={(): void => setModal(null)} onConfirm={open} />); + return; } + + desktopApp.openInternalVideoChatWindow(callUrl, undefined); }, [setModal], ); diff --git a/apps/meteor/client/views/teams/ConvertToChannelModal/ConvertToChannelModal.tsx b/apps/meteor/client/views/teams/ConvertToChannelModal/ConvertToChannelModal.tsx index 7acf1a337a04..1cf2698b4834 100644 --- a/apps/meteor/client/views/teams/ConvertToChannelModal/ConvertToChannelModal.tsx +++ b/apps/meteor/client/views/teams/ConvertToChannelModal/ConvertToChannelModal.tsx @@ -1,10 +1,8 @@ import type { IRoom, Serialized } from '@rocket.chat/core-typings'; -import { Skeleton } from '@rocket.chat/fuselage'; -import { useTranslation } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { useMemo } from 'react'; -import GenericModal from '../../../components/GenericModal'; +import GenericModalSkeleton from '../../../components/GenericModal/GenericModalSkeleton'; import { useEndpointData } from '../../../hooks/useEndpointData'; import { AsyncStatePhase } from '../../../lib/asyncState'; import BaseConvertToChannelModal from './BaseConvertToChannelModal'; @@ -18,18 +16,12 @@ type ConvertToChannelModalProps = { }; const ConvertToChannelModal: FC<ConvertToChannelModalProps> = ({ onClose, onCancel, onConfirm, teamId, userId }) => { - const t = useTranslation(); - const { value, phase } = useEndpointData('/v1/teams.listRoomsOfUser', { params: useMemo(() => ({ teamId, userId, canUserDelete: 'true' }), [teamId, userId]), }); if (phase === AsyncStatePhase.LOADING) { - return ( - <GenericModal variant='warning' onClose={onClose} title={<Skeleton width='50%' />} confirmText={t('Cancel')} onConfirm={onClose}> - <Skeleton width='full' /> - </GenericModal> - ); + return <GenericModalSkeleton onClose={onClose} />; } return <BaseConvertToChannelModal onClose={onClose} onCancel={onCancel} onConfirm={onConfirm} rooms={value?.rooms} />; diff --git a/apps/meteor/ee/app/license/server/bundles.ts b/apps/meteor/ee/app/license/server/bundles.ts index 8eb80d4b8f01..507283b3e60f 100644 --- a/apps/meteor/ee/app/license/server/bundles.ts +++ b/apps/meteor/ee/app/license/server/bundles.ts @@ -14,7 +14,8 @@ export type BundleFeature = | 'oauth-enterprise' | 'federation' | 'videoconference-enterprise' - | 'message-read-receipt'; + | 'message-read-receipt' + | 'outlook-calendar'; interface IBundle { [key: string]: BundleFeature[]; @@ -38,6 +39,7 @@ const bundles: IBundle = { 'federation', 'videoconference-enterprise', 'message-read-receipt', + 'outlook-calendar', ], pro: [], }; diff --git a/apps/meteor/ee/server/configuration/index.ts b/apps/meteor/ee/server/configuration/index.ts index badf718504ec..9a7738b23e4a 100644 --- a/apps/meteor/ee/server/configuration/index.ts +++ b/apps/meteor/ee/server/configuration/index.ts @@ -1,4 +1,5 @@ import './ldap'; import './oauth'; +import './outlookCalendar'; import './saml'; import './videoConference'; diff --git a/apps/meteor/ee/server/configuration/outlookCalendar.ts b/apps/meteor/ee/server/configuration/outlookCalendar.ts new file mode 100644 index 000000000000..15363ea9e5ae --- /dev/null +++ b/apps/meteor/ee/server/configuration/outlookCalendar.ts @@ -0,0 +1,13 @@ +import { Meteor } from 'meteor/meteor'; +import { Calendar } from '@rocket.chat/core-services'; + +import { onLicense } from '../../app/license/server'; +import { addSettings } from '../settings/outlookCalendar'; + +Meteor.startup(() => + onLicense('outlook-calendar', async () => { + addSettings(); + + await Calendar.setupNextNotification(); + }), +); diff --git a/apps/meteor/ee/server/settings/outlookCalendar.ts b/apps/meteor/ee/server/settings/outlookCalendar.ts new file mode 100644 index 000000000000..02dd51f79510 --- /dev/null +++ b/apps/meteor/ee/server/settings/outlookCalendar.ts @@ -0,0 +1,41 @@ +import { settingsRegistry } from '../../../app/settings/server'; + +export function addSettings(): void { + void settingsRegistry.addGroup('Outlook_Calendar', async function () { + await this.with( + { + enterprise: true, + modules: ['outlook-calendar'], + }, + async function () { + await this.add('Outlook_Calendar_Enabled', false, { + type: 'boolean', + public: true, + invalidValue: false, + }); + + await this.add('Outlook_Calendar_Exchange_Url', '', { + type: 'string', + public: true, + invalidValue: '', + }); + + await this.add('Outlook_Calendar_Outlook_Url', '', { + type: 'string', + public: true, + invalidValue: '', + }); + + await this.add( + 'Calendar_MeetingUrl_Regex', + '(?:[?&]callUrl=([^\n&<]+))|(?:(?:%3F)|(?:%26))callUrl(?:%3D)((?:(?:[^\n&<](?!%26)))+[^\n&<]?)', + { + type: 'string', + public: true, + invalidValue: '', + }, + ); + }, + ); + }); +} diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 60b02e3ca684..ec6c27d3799b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -727,6 +727,7 @@ "Away": "Away", "Back": "Back", "Back_to_applications": "Back to applications", + "Back_to_calendar": "Back to calendar", "Back_to_chat": "Back to chat", "Back_to_imports": "Back to imports", "Back_to_integration_detail": "Back to the integration detail", @@ -817,6 +818,9 @@ "By": "By", "by": "by", "cache_cleared": "Cache cleared", + "Calendar_MeetingUrl_Regex": "Meeting url Regular Expression", + "Calendar_MeetingUrl_Regex_Description": "Expression used to detect meeting URLs in event descriptions. The first matching group with a valid url will be used. HTML encoded urls will be decoded automatically.", + "Calendar_settings": "Calendar settings", "Call": "Call", "Call_again": "Call again", "Call_back": "Call back", @@ -2072,6 +2076,8 @@ "Esc_to": "Esc to", "Estimated_wait_time": "Estimated wait time", "Estimated_wait_time_in_minutes": "Estimated wait time (time in minutes)", + "Event_notifications": "Event notifications", + "Event_notifications_description": "By disabling this setting you’ll prevent the app from notifying you of upcoming events.", "Event_Trigger": "Event Trigger", "Event_Trigger_Description": "Select which type of event will trigger this Outgoing WebHook Integration", "every_5_minutes": "Once every 5 minutes", @@ -3156,6 +3162,7 @@ "Logged_Out_Banner_Text": "Your workspace admin ended your session on this device. Please log in again to continue.", "Logged_out_of_other_clients_successfully": "Logged out of other clients successfully", "Login": "Login", + "Log_in_to_sync": "Log in to sync", "Login_Attempts": "Failed Login Attempts", "Login_Detected": "Login detected", "Logged_In_Via": "Logged in via", @@ -3612,6 +3619,7 @@ "No_Canned_Responses_Yet-description": "Use canned responses to provide quick and consistent answers to frequently asked questions.", "No_channels_in_team": "No Channels on this Team", "No_channels_yet": "You aren't part of any channels yet", + "No_content_was_provided": "No content was provided", "No_data_found": "No data found", "No_direct_messages_yet": "No Direct Messages.", "No_Discussions_found": "No discussions found", @@ -3679,6 +3687,7 @@ "Notifications_Sound_Volume": "Notifications sound volume", "Notify_active_in_this_room": "Notify active users in this room", "Notify_all_in_this_room": "Notify all in this room", + "Notify_Calendar_Events": "Notify calendar events", "Now_Its_Visible_For_Everyone": "Now it's visible for everyone", "Now_Its_Visible_Only_For_Admins": "Now it's visible only for admins", "NPS_survey_enabled": "Enable NPS Survey", @@ -3779,6 +3788,7 @@ "Open_directory": "Open directory", "Open_Livechats": "Chats in Progress", "Open_menu": "Open_menu", + "Open_Outlook": "Open Outlook", "Open_settings": "Open settings", "Open-source_conference_call_solution": "Open-source conference call solution.", "Open_thread": "Open Thread", @@ -3829,7 +3839,22 @@ "Outgoing": "Outgoing", "Outgoing_WebHook": "Outgoing WebHook", "Outgoing_WebHook_Description": "Get data out of Rocket.Chat in real-time.", + "Outlook_authentication": "Outlook authentication", + "Outlook_authentication_disabled": "Outlook authentication disabled", + + "Outlook_authentication_description": "Disable this to clear any outlook credentials stored in this machine.", + "Outlook_calendar": "Outlook calendar", + "Outlook_calendar_event": "Outlook calendar event", + "Outlook_calendar_settings": "Outlook calendar settings", + "Outlook_Calendar": "Outlook Calendar", + "Outlook_Calendar_Enabled": "Enabled", + "Outlook_Calendar_Exchange_Url": "Exchange URL", + "Outlook_Calendar_Exchange_Url_Description": "Host URL for the EWS api.", + "Outlook_Calendar_Outlook_Url": "Outlook URL", + "Outlook_Calendar_Outlook_Url_Description": "URL used to launch the Outlook web app.", "Output_format": "Output format", + "Outlook_Sync_Failed": "Failed to load outlook events.", + "Outlook_Sync_Success": "Outlook events synchronized.", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL to which files are uploaded. This url also used for downloads unless a CDN is given", "Override_Destination_Channel": "Allow to overwrite destination channel in the body parameters", "Owner": "Owner", @@ -4100,6 +4125,7 @@ "Reload": "Reload", "Reload_page": "Reload Page", "Reload_Pages": "Reload Pages", + "Remember_my_credentials": "Remember my credentials", "Remove": "Remove", "Remove_Admin": "Remove Admin", "Remove_Association": "Remove Association", diff --git a/apps/meteor/server/methods/saveUserPreferences.ts b/apps/meteor/server/methods/saveUserPreferences.ts index 9e974673f24a..0cb1dc922eea 100644 --- a/apps/meteor/server/methods/saveUserPreferences.ts +++ b/apps/meteor/server/methods/saveUserPreferences.ts @@ -36,6 +36,7 @@ type UserPreferences = { dontAskAgainList: { action: string; label: string }[]; themeAppearence: 'auto' | 'light' | 'dark'; receiveLoginDetectionEmail: boolean; + notifyCalendarEvents: boolean; }; declare module '@rocket.chat/ui-contexts' { @@ -78,6 +79,7 @@ export const saveUserPreferences = async (settings: Partial<UserPreferences>, us muteFocusedConversations: Match.Optional(Boolean), omnichannelTranscriptEmail: Match.Optional(Boolean), omnichannelTranscriptPDF: Match.Optional(Boolean), + notifyCalendarEvents: Match.Optional(Boolean), }; check(settings, Match.ObjectIncluding(keys)); const user = await Users.findOneById(userId); diff --git a/apps/meteor/server/models/CalendarEvent.ts b/apps/meteor/server/models/CalendarEvent.ts new file mode 100644 index 000000000000..669ecbbdb91c --- /dev/null +++ b/apps/meteor/server/models/CalendarEvent.ts @@ -0,0 +1,6 @@ +import { registerModel } from '@rocket.chat/models'; + +import { db } from '../database/utils'; +import { CalendarEventRaw } from './raw/CalendarEvent'; + +registerModel('ICalendarEventModel', new CalendarEventRaw(db)); diff --git a/apps/meteor/server/models/raw/CalendarEvent.ts b/apps/meteor/server/models/raw/CalendarEvent.ts new file mode 100644 index 000000000000..7a071c77e247 --- /dev/null +++ b/apps/meteor/server/models/raw/CalendarEvent.ts @@ -0,0 +1,124 @@ +import type { FindCursor, IndexDescription, Collection, Db, UpdateResult } from 'mongodb'; +import type { ICalendarEvent, IUser, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; +import type { ICalendarEventModel } from '@rocket.chat/model-typings'; + +import { BaseRaw } from './BaseRaw'; + +export class CalendarEventRaw extends BaseRaw<ICalendarEvent> implements ICalendarEventModel { + constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<ICalendarEvent>>) { + super(db, 'calendar_event', trash); + } + + protected modelIndexes(): IndexDescription[] { + return [ + { + key: { startTime: -1, uid: 1, externalId: 1 }, + }, + { + key: { reminderTime: -1, notificationSent: 1 }, + }, + ]; + } + + public async findOneByExternalIdAndUserId( + externalId: Required<ICalendarEvent>['externalId'], + uid: ICalendarEvent['uid'], + ): Promise<ICalendarEvent | null> { + return this.findOne({ + externalId, + uid, + }); + } + + public findByUserIdAndDate(uid: IUser['_id'], date: Date): FindCursor<ICalendarEvent> { + const startTime = new Date(date.toISOString()); + startTime.setHours(0, 0, 0, 0); + + const finalTime = new Date(date.valueOf()); + finalTime.setDate(finalTime.getDate() + 1); + + return this.find( + { + uid, + startTime: { $gte: startTime, $lt: finalTime }, + }, + { + sort: { startTime: 1 }, + }, + ); + } + + public async updateEvent( + eventId: ICalendarEvent['_id'], + { subject, description, startTime, meetingUrl, reminderMinutesBeforeStart, reminderTime }: Partial<ICalendarEvent>, + ): Promise<UpdateResult> { + return this.updateOne( + { _id: eventId }, + { + $set: { + ...(subject !== undefined ? { subject } : {}), + ...(description !== undefined ? { description } : {}), + ...(startTime ? { startTime } : {}), + ...(meetingUrl !== undefined ? { meetingUrl } : {}), + ...(reminderMinutesBeforeStart ? { reminderMinutesBeforeStart } : {}), + ...(reminderTime ? { reminderTime } : {}), + }, + }, + ); + } + + public async findNextNotificationDate(): Promise<Date | null> { + const nextEvent = await this.findOne<Pick<ICalendarEvent, 'reminderTime'>>( + { + reminderTime: { + $gt: new Date(), + }, + notificationSent: false, + }, + { + sort: { + reminderTime: 1, + }, + projection: { + reminderTime: 1, + }, + }, + ); + + return nextEvent?.reminderTime || null; + } + + public findEventsToNotify(notificationTime: Date, minutes: number): FindCursor<ICalendarEvent> { + // Find all the events between notificationTime and +minutes that have not been notified yet + const maxDate = new Date(notificationTime.toISOString()); + maxDate.setMinutes(maxDate.getMinutes() + minutes); + + return this.find( + { + reminderTime: { + $gte: notificationTime, + $lt: maxDate, + }, + notificationSent: false, + }, + { + sort: { + reminderTime: 1, + }, + }, + ); + } + + public async flagNotificationSent(eventId: ICalendarEvent['_id']): Promise<UpdateResult> { + return this.updateOne( + { + _id: eventId, + }, + { + $set: { + notificationSent: true, + }, + }, + ); + } +} diff --git a/apps/meteor/server/models/startup.ts b/apps/meteor/server/models/startup.ts index f9c5fa1be2a0..8e83683869ac 100644 --- a/apps/meteor/server/models/startup.ts +++ b/apps/meteor/server/models/startup.ts @@ -5,6 +5,7 @@ import './AppsPersistence'; import './Avatars'; import './Banners'; import './BannersDismiss'; +import './CalendarEvent'; import './CredentialTokens'; import './CustomSounds'; import './CustomUserStatus'; diff --git a/apps/meteor/server/modules/listeners/listeners.module.ts b/apps/meteor/server/modules/listeners/listeners.module.ts index 45d8e75952fb..b013d5fe3b56 100644 --- a/apps/meteor/server/modules/listeners/listeners.module.ts +++ b/apps/meteor/server/modules/listeners/listeners.module.ts @@ -391,6 +391,10 @@ export class ListenersModule { notifications.notifyAllInThisInstance('updateCustomSound', data); }); + service.onEvent('notify.calendar', (uid, data): void => { + notifications.notifyUserInThisInstance(uid, 'calendar', data); + }); + service.onEvent('connector.statuschanged', (enabled): void => { notifications.notifyLoggedInThisInstance('voip.statuschanged', enabled); }); diff --git a/apps/meteor/server/services/calendar/service.ts b/apps/meteor/server/services/calendar/service.ts new file mode 100644 index 000000000000..bfe357e1566a --- /dev/null +++ b/apps/meteor/server/services/calendar/service.ts @@ -0,0 +1,233 @@ +import type { UpdateResult, DeleteResult } from 'mongodb'; +import type { IUser, ICalendarEvent } from '@rocket.chat/core-typings'; +import type { InsertionModel } from '@rocket.chat/model-typings'; +import { CalendarEvent } from '@rocket.chat/models'; +import type { ICalendarService } from '@rocket.chat/core-services'; +import { ServiceClassInternal, api } from '@rocket.chat/core-services'; +import { cronJobs } from '@rocket.chat/cron'; + +import { settings } from '../../../app/settings/server'; +import { getUserPreference } from '../../../app/utils/server/lib/getUserPreference'; +import { Logger } from '../../lib/logger/Logger'; + +const logger = new Logger('Calendar'); + +const defaultMinutesForNotifications = 5; + +export class CalendarService extends ServiceClassInternal implements ICalendarService { + protected name = 'calendar'; + + public async create(data: Omit<InsertionModel<ICalendarEvent>, 'reminderTime' | 'notificationSent'>): Promise<ICalendarEvent['_id']> { + const { uid, startTime, subject, description, reminderMinutesBeforeStart, meetingUrl } = data; + + const minutes = reminderMinutesBeforeStart ?? defaultMinutesForNotifications; + const reminderTime = minutes ? this.getShiftedTime(startTime, -minutes) : undefined; + + const insertData: InsertionModel<ICalendarEvent> = { + uid, + startTime, + subject, + description, + meetingUrl, + reminderMinutesBeforeStart: minutes, + reminderTime, + notificationSent: false, + }; + + const insertResult = await CalendarEvent.insertOne(insertData); + await this.setupNextNotification(); + + return insertResult.insertedId; + } + + public async import(data: Omit<InsertionModel<ICalendarEvent>, 'notificationSent'>): Promise<ICalendarEvent['_id']> { + const { externalId } = data; + if (!externalId) { + return this.create(data); + } + + const { uid, startTime, subject, description, reminderMinutesBeforeStart } = data; + const meetingUrl = data.meetingUrl ? data.meetingUrl : await this.parseDescriptionForMeetingUrl(description); + const reminderTime = reminderMinutesBeforeStart ? this.getShiftedTime(startTime, -reminderMinutesBeforeStart) : undefined; + + const updateData: Omit<InsertionModel<ICalendarEvent>, 'uid' | 'notificationSent'> = { + startTime, + subject, + description, + meetingUrl, + reminderMinutesBeforeStart, + reminderTime, + externalId, + }; + + const event = await this.findImportedEvent(externalId, uid); + + if (!event) { + const insertResult = await CalendarEvent.insertOne({ + uid, + notificationSent: false, + ...updateData, + }); + + await this.setupNextNotification(); + return insertResult.insertedId; + } + + const updateResult = await CalendarEvent.updateEvent(event._id, updateData); + if (updateResult.modifiedCount > 0) { + await this.setupNextNotification(); + } + + return event._id; + } + + public async get(eventId: ICalendarEvent['_id']): Promise<ICalendarEvent | null> { + return CalendarEvent.findOne({ _id: eventId }); + } + + public async list(uid: IUser['_id'], date: Date): Promise<ICalendarEvent[]> { + return CalendarEvent.findByUserIdAndDate(uid, date).toArray(); + } + + public async update(eventId: ICalendarEvent['_id'], data: Partial<ICalendarEvent>): Promise<UpdateResult> { + const { startTime, subject, description, reminderMinutesBeforeStart } = data; + const meetingUrl = data.meetingUrl ? data.meetingUrl : await this.parseDescriptionForMeetingUrl(description || ''); + const reminderTime = reminderMinutesBeforeStart && startTime ? this.getShiftedTime(startTime, -reminderMinutesBeforeStart) : undefined; + + const updateData: Partial<ICalendarEvent> = { + startTime, + subject, + description, + meetingUrl, + reminderMinutesBeforeStart, + reminderTime, + }; + + const updateResult = await CalendarEvent.updateEvent(eventId, updateData); + + if (updateResult.modifiedCount > 0) { + await this.setupNextNotification(); + } + + return updateResult; + } + + public async delete(eventId: ICalendarEvent['_id']): Promise<DeleteResult> { + return CalendarEvent.deleteOne({ + _id: eventId, + }); + } + + public async setupNextNotification(): Promise<void> { + return this.doSetupNextNotification(false); + } + + private async doSetupNextNotification(isRecursive: boolean): Promise<void> { + const date = await CalendarEvent.findNextNotificationDate(); + if (!date) { + if (await cronJobs.has('calendar-reminders')) { + await cronJobs.remove('calendar-reminders'); + } + return; + } + + date.setSeconds(0); + if (!isRecursive && date.valueOf() < Date.now()) { + return this.sendCurrentNotifications(date); + } + + await cronJobs.addAtTimestamp('calendar-reminders', date, async () => this.sendCurrentNotifications(date)); + } + + public async sendCurrentNotifications(date: Date): Promise<void> { + const events = await CalendarEvent.findEventsToNotify(date, 1).toArray(); + + for await (const event of events) { + await this.sendEventNotification(event); + + await CalendarEvent.flagNotificationSent(event._id); + } + + await this.doSetupNextNotification(true); + } + + public async sendEventNotification(event: ICalendarEvent): Promise<void> { + if (!(await getUserPreference(event.uid, 'notifyCalendarEvents'))) { + return; + } + + return api.broadcast('notify.calendar', event.uid, { + title: event.subject, + text: event.startTime.toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric', dayPeriod: 'narrow' }), + payload: { + _id: event._id, + }, + }); + } + + public async findImportedEvent( + externalId: Required<ICalendarEvent>['externalId'], + uid: ICalendarEvent['uid'], + ): Promise<ICalendarEvent | null> { + return CalendarEvent.findOneByExternalIdAndUserId(externalId, uid); + } + + public async parseDescriptionForMeetingUrl(description: string): Promise<string | undefined> { + if (!description) { + return; + } + + const defaultPattern = '(?:[?&]callUrl=([^\n&<]+))|(?:(?:%3F)|(?:%26))callUrl(?:%3D)((?:(?:[^\n&<](?!%26)))+[^\n&<]?)'; + const pattern = (settings.get<string>('Calendar_MeetingUrl_Regex') || defaultPattern).trim(); + + if (!pattern) { + return; + } + + const regex: RegExp | undefined = (() => { + try { + return new RegExp(pattern, 'im'); + } catch { + logger.error('Failed to parse regular expression for meeting url.'); + } + })(); + + if (!regex) { + return; + } + + const results = description.match(regex); + if (!results) { + return; + } + + const [, ...urls] = results; + for (const encodedUrl of urls) { + if (!encodedUrl) { + continue; + } + + let url = encodedUrl; + while (!url.includes('://')) { + const decodedUrl = decodeURIComponent(url); + if (decodedUrl === url) { + break; + } + + url = decodedUrl; + } + + if (url.includes('://')) { + return url; + } + } + + return undefined; + } + + private getShiftedTime(time: Date, minutes: number): Date { + const newTime = new Date(time.valueOf()); + newTime.setMinutes(newTime.getMinutes() + minutes); + return newTime; + } +} diff --git a/apps/meteor/server/services/startup.ts b/apps/meteor/server/services/startup.ts index 004d4f25a4b4..2afc502846d0 100644 --- a/apps/meteor/server/services/startup.ts +++ b/apps/meteor/server/services/startup.ts @@ -6,6 +6,7 @@ import { AnalyticsService } from './analytics/service'; import { AppsEngineService } from './apps-engine/service'; import { AuthorizationLivechat } from '../../app/livechat/server/roomAccessValidator.internalService'; import { BannerService } from './banner/service'; +import { CalendarService } from './calendar/service'; import { LDAPService } from './ldap/service'; import { MediaService } from './image/service'; import { MeteorService } from './meteor/service'; @@ -34,6 +35,7 @@ api.registerService(new AppsEngineService()); api.registerService(new AnalyticsService()); api.registerService(new AuthorizationLivechat()); api.registerService(new BannerService()); +api.registerService(new CalendarService()); api.registerService(new LDAPService()); api.registerService(new MediaService()); api.registerService(new MeteorService()); diff --git a/apps/meteor/server/settings/accounts.ts b/apps/meteor/server/settings/accounts.ts index 76f1c344b94f..586795cef638 100644 --- a/apps/meteor/server/settings/accounts.ts +++ b/apps/meteor/server/settings/accounts.ts @@ -686,6 +686,12 @@ export const createAccountSettings = () => public: true, i18nLabel: 'Omnichannel_transcript_email', }); + + await this.add('Accounts_Default_User_Preferences_notifyCalendarEvents', true, { + type: 'boolean', + public: true, + i18nLabel: 'Notify_Calendar_Events', + }); }); await this.section('Avatar', async function () { diff --git a/apps/meteor/tests/data/user.ts b/apps/meteor/tests/data/user.ts index 547809fae4d2..3587f2d495d6 100644 --- a/apps/meteor/tests/data/user.ts +++ b/apps/meteor/tests/data/user.ts @@ -30,6 +30,7 @@ export const preferences = { hideFlexTab: false, sendOnEnter: 'normal', idleTimeLimit: 3600, + notifyCalendarEvents: false, }, }; diff --git a/apps/meteor/tests/end-to-end/api/00-miscellaneous.js b/apps/meteor/tests/end-to-end/api/00-miscellaneous.js index 971ed7d8e7b8..500e3c4cd332 100644 --- a/apps/meteor/tests/end-to-end/api/00-miscellaneous.js +++ b/apps/meteor/tests/end-to-end/api/00-miscellaneous.js @@ -173,6 +173,7 @@ describe('miscellaneous', function () { 'sidebarDisplayAvatar', 'sidebarGroupByType', 'muteFocusedConversations', + 'notifyCalendarEvents', ].filter((p) => Boolean(p)); expect(res.body).to.have.property('success', true); diff --git a/apps/meteor/tests/end-to-end/api/30-calendar.ts b/apps/meteor/tests/end-to-end/api/30-calendar.ts new file mode 100644 index 000000000000..9074400610b4 --- /dev/null +++ b/apps/meteor/tests/end-to-end/api/30-calendar.ts @@ -0,0 +1,660 @@ +import { expect } from 'chai'; +import type { Response } from 'supertest'; + +import { createUser, login } from '../../data/users.helper'; +import { getCredentials, api, request, credentials } from '../../data/api-data.js'; +import { password } from '../../data/user'; + +describe('[Calendar Events]', function () { + this.retries(0); + + let user2: Awaited<ReturnType<typeof createUser>> | undefined; + let userCredentials: Awaited<ReturnType<typeof login>> | undefined; + + before((done) => getCredentials(done)); + + before(async () => { + user2 = await createUser(); + userCredentials = await login(user2.username, password); + }); + + describe('[/calendar-events.create]', () => { + it('should successfully create an event in the calendar', async function () { + let eventId: string | undefined; + + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Subject', + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + eventId = res.body.id; + }); + + after(async function () { + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); + }); + }); + + it('should fail to create an event without a start time', async function () { + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + subject: 'Subject', + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail to create an event without a subject', async function () { + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + description: 'Description', + startTime: new Date().toISOString(), + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail to create an event without a description', async function () { + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + subject: 'Subject', + startTime: new Date().toISOString(), + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should successfully create an event without reminder information', async function () { + let eventId: string | undefined; + + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Subject', + description: 'Description', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + eventId = res.body.id; + }); + + after(async function () { + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); + }); + }); + }); + + describe('[/calendar-events.list]', () => { + const testSubject = `calendar-events.list-${Date.now()}`; + const testSubject2 = `calendar-events.list-${Date.now()}`; + let eventId: string | undefined; + let eventId2: string | undefined; + let eventId3: string | undefined; + + before('create sample events', async () => { + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: testSubject, + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .then((res) => { + eventId = res.body.id; + }); + + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + startTime: new Date(Date.now() + 48 * 60 * 60 * 1000).toISOString(), + subject: testSubject2, + description: 'Future Event', + reminderMinutesBeforeStart: 10, + }) + .then((res) => { + eventId2 = res.body.id; + }); + + await request + .post(api('calendar-events.create')) + .set(userCredentials) + .send({ + startTime: new Date().toISOString(), + subject: testSubject, + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .then((res) => { + eventId3 = res.body.id; + }); + }); + + after(async function () { + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId: eventId2 }); + await request.post(api('calendar-events.delete')).set(userCredentials).send({ eventId: eventId3 }); + }); + + it('should list only the events with the same date', async function () { + await request + .get(api('calendar-events.list')) + .set(credentials) + .query({ + date: new Date().toISOString(), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('data').that.is.an('array'); + + const events = res.body.data.map((event: any) => event._id); + expect(events).to.be.an('array').that.includes(eventId); + expect(events).to.not.includes(eventId2); + }); + }); + + it('should nost list events from other users', async function () { + await request + .get(api('calendar-events.list')) + .set(userCredentials) + .query({ + date: new Date().toISOString(), + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('data').that.is.an('array'); + + const events = res.body.data.map((event: any) => event._id); + expect(events).to.be.an('array').that.includes(eventId3); + expect(events).to.not.includes(eventId); + }); + }); + }); + + describe('[/calendar-events.info]', () => { + const testSubject = `calendar-events.info-${Date.now()}`; + let eventId: string | undefined; + let eventId2: string | undefined; + + before('create sample events', async () => { + await request + .post(api('calendar-events.create')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: testSubject, + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .then((res) => { + eventId = res.body.id; + }); + + await request + .post(api('calendar-events.create')) + .set(userCredentials) + .send({ + startTime: new Date().toISOString(), + subject: testSubject, + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .then((res) => { + eventId2 = res.body.id; + }); + }); + + after(async function () { + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); + await request.post(api('calendar-events.delete')).set(userCredentials).send({ eventId: eventId2 }); + }); + + it('should return the event information', async function () { + await request + .get(api('calendar-events.info')) + .set(credentials) + .query({ + id: eventId, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('event').that.is.an('object').with.property('subject', testSubject); + }); + }); + + it('should return the event information - regular user', async function () { + await request + .get(api('calendar-events.info')) + .set(userCredentials) + .query({ + id: eventId2, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('event').that.is.an('object').with.property('subject', testSubject); + }); + }); + + it('should fail when querying an invalid event', async function () { + await request + .get(api('calendar-events.info')) + .set(credentials) + .query({ + id: 'something-random', + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail when querying an event from another user', async function () { + await request + .get(api('calendar-events.info')) + .set(credentials) + .query({ + id: eventId2, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + }); + + describe('[/calendar-events.import]', () => { + it('should successfully import an event to the calendar', async function () { + let eventId: string | undefined; + const externalId = `calendar-events.import-${Date.now()}`; + + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Subject', + description: 'Description', + reminderMinutesBeforeStart: 10, + externalId, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + eventId = res.body.id; + }); + + after(async function () { + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); + }); + }); + + it('should fail to import an event without an external id', async function () { + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Subject', + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail to import an event without a start time', async function () { + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + subject: 'Subject', + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail to import an event without a subject', async function () { + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + description: 'Description', + startTime: new Date().toISOString(), + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail to import an event without a description', async function () { + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + subject: 'Subject', + startTime: new Date().toISOString(), + reminderMinutesBeforeStart: 10, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should successfully import an event without reminder information', async function () { + let eventId: string | undefined; + + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Subject', + description: 'Description', + externalId: `calendar-events.import-external-id-${Date.now()}`, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + eventId = res.body.id; + }); + + after(async function () { + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); + }); + }); + + it('should import a new event even if it was already imported by another user', async function () { + let eventId: string | undefined; + let eventId2: string | undefined; + const externalId = `calendar-events.import-${Date.now()}`; + + after(async function () { + await request.post(api('calendar-events.delete')).set(userCredentials).send({ eventId }); + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId: eventId2 }); + }); + + await request + .post(api('calendar-events.import')) + .set(userCredentials) + .send({ + startTime: new Date().toISOString(), + subject: 'First User', + description: 'Description', + reminderMinutesBeforeStart: 10, + externalId, + }) + .then((res) => { + eventId = res.body.id; + }); + + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Second User', + description: 'Description', + reminderMinutesBeforeStart: 10, + externalId, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body.id).to.not.be.equal(eventId); + eventId2 = res.body.id; + }); + + await request + .get(api('calendar-events.info')) + .set(userCredentials) + .query({ id: eventId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('event').that.is.an('object').with.property('subject', 'First User'); + }); + + await request + .get(api('calendar-events.info')) + .set(credentials) + .query({ id: eventId2 }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('event').that.is.an('object').with.property('subject', 'Second User'); + }); + }); + + it('should update an event that has the same external id', async function () { + let eventId: string | undefined; + const externalId = `calendar-events.import-twice-${Date.now()}`; + + after(async function () { + await request.post(api('calendar-events.delete')).set(credentials).send({ eventId }); + }); + + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Subject', + description: 'Description', + reminderMinutesBeforeStart: 10, + externalId, + }) + .then((res) => { + eventId = res.body.id; + }); + + await request + .post(api('calendar-events.import')) + .set(credentials) + .send({ + startTime: new Date().toISOString(), + subject: 'New Subject', + description: 'New Description', + reminderMinutesBeforeStart: 15, + externalId, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect(async (res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('id', eventId); + }); + + await request + .get(api('calendar-events.info')) + .set(credentials) + .query({ id: eventId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('event').that.is.an('object').with.property('subject', 'New Subject'); + }); + }); + }); + + describe('[/calendar-events.update]', () => { + const testSubject = `calendar-events.update-${Date.now()}`; + let eventId: string | undefined; + + before('create sample events', async () => { + await request + .post(api('calendar-events.create')) + .set(userCredentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Old Subject', + description: 'Old Description', + reminderMinutesBeforeStart: 10, + }) + .then((res) => { + eventId = res.body.id; + }); + }); + + after(async function () { + await request.post(api('calendar-events.delete')).set(userCredentials).send({ eventId }); + }); + + it('should update the event with the new data', async function () { + await request + .post(api('calendar-events.update')) + .set(userCredentials) + .send({ + eventId, + startTime: new Date().toISOString(), + subject: testSubject, + description: 'New Description', + reminderMinutesBeforeStart: 15, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect(async (res: Response) => { + expect(res.body).to.have.property('success', true); + }); + + await request + .get(api('calendar-events.info')) + .set(userCredentials) + .query({ id: eventId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('event').that.is.an('object'); + expect(res.body.event).to.have.property('subject', testSubject); + expect(res.body.event).to.have.property('description', 'New Description'); + }); + }); + + it('should fail to update an event that doesnt exist', async function () { + await request + .post(api('calendar-events.update')) + .set(userCredentials) + .send({ + eventId: 'something-random', + startTime: new Date().toISOString(), + subject: testSubject, + description: 'New Description', + reminderMinutesBeforeStart: 15, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail to update an event from another user', async function () { + await request + .post(api('calendar-events.update')) + .set(credentials) + .send({ + eventId, + startTime: new Date().toISOString(), + subject: 'Another Subject', + description: 'Third Description', + reminderMinutesBeforeStart: 20, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + }); + + describe('[/calendar-events.delete]', () => { + let eventId: string | undefined; + + before('create sample events', async () => { + await request + .post(api('calendar-events.create')) + .set(userCredentials) + .send({ + startTime: new Date().toISOString(), + subject: 'Subject', + description: 'Description', + reminderMinutesBeforeStart: 10, + }) + .then((res) => { + eventId = res.body.id; + }); + }); + + it('should fail to delete an event from another user', async function () { + await request + .post(api('calendar-events.delete')) + .set(credentials) + .send({ + eventId, + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should delete the specified event', async function () { + await request + .post(api('calendar-events.delete')) + .set(userCredentials) + .send({ + eventId, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect(async (res: Response) => { + expect(res.body).to.have.property('success', true); + }); + + await request + .get(api('calendar-events.info')) + .set(userCredentials) + .query({ id: eventId }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + + it('should fail to delete an event that doesnt exist', async function () { + await request + .post(api('calendar-events.delete')) + .set(userCredentials) + .send({ + eventId: 'something-random', + }) + .expect('Content-Type', 'application/json') + .expect(400); + }); + }); +}); diff --git a/packages/core-services/src/Events.ts b/packages/core-services/src/Events.ts index 56187608df00..838e60f84716 100644 --- a/packages/core-services/src/Events.ts +++ b/packages/core-services/src/Events.ts @@ -27,6 +27,7 @@ import type { UserStatus, ILivechatPriority, VideoConference, + ICalendarNotification, AtLeast, ILivechatInquiryRecord, } from '@rocket.chat/core-typings'; @@ -83,6 +84,7 @@ export type EventSignatures = { ): void; 'notify.deleteCustomSound'(data: { soundData: ICustomSound }): void; 'notify.updateCustomSound'(data: { soundData: ICustomSound }): void; + 'notify.calendar'(uid: string, data: ICalendarNotification): void; 'permission.changed'(data: { clientAction: ClientAction; data: any }): void; 'room'(data: { action: string; room: Partial<IRoom> }): void; 'room.avatarUpdate'(room: Pick<IRoom, '_id' | 'avatarETag'>): void; diff --git a/packages/core-services/src/index.ts b/packages/core-services/src/index.ts index 22f7a82b3914..3352c0d3d373 100644 --- a/packages/core-services/src/index.ts +++ b/packages/core-services/src/index.ts @@ -36,6 +36,7 @@ import type { IDeviceManagementService } from './types/IDeviceManagementService' import type { IPushService } from './types/IPushService'; import type { IOmnichannelService } from './types/IOmnichannelService'; import type { ITelemetryEvent, TelemetryMap, TelemetryEvents } from './types/ITelemetryEvent'; +import type { ICalendarService } from './types/ICalendarService'; import type { IOmnichannelTranscriptService } from './types/IOmnichannelTranscriptService'; import type { IQueueWorkerService, HealthAggResult } from './types/IQueueWorkerService'; import type { ITranslationService } from './types/ITranslationService'; @@ -108,6 +109,7 @@ export { ISendFileMessageParams, IUploadFileParams, IUploadService, + ICalendarService, IOmnichannelTranscriptService, IQueueWorkerService, HealthAggResult, @@ -140,6 +142,7 @@ export const SAUMonitor = proxifyWithWait<ISAUMonitorService>('sau-monitor'); export const DeviceManagement = proxifyWithWait<IDeviceManagementService>('device-management'); export const VideoConf = proxifyWithWait<IVideoConfService>('video-conference'); export const Upload = proxifyWithWait<IUploadService>('upload'); +export const Calendar = proxifyWithWait<ICalendarService>('calendar'); export const QueueWorker = proxifyWithWait<IQueueWorkerService>('queue-worker'); export const OmnichannelTranscript = proxifyWithWait<IOmnichannelTranscriptService>('omnichannel-transcript'); export const Message = proxifyWithWait<IMessageService>('message'); diff --git a/packages/core-services/src/types/ICalendarService.ts b/packages/core-services/src/types/ICalendarService.ts new file mode 100644 index 000000000000..114ce8551065 --- /dev/null +++ b/packages/core-services/src/types/ICalendarService.ts @@ -0,0 +1,15 @@ +import type { UpdateResult, DeleteResult } from 'mongodb'; +import type { ICalendarEvent, IUser } from '@rocket.chat/core-typings'; +import type { InsertionModel } from '@rocket.chat/model-typings'; + +export interface ICalendarService { + create(data: Omit<InsertionModel<ICalendarEvent>, 'reminderTime' | 'notificationSent'>): Promise<ICalendarEvent['_id']>; + import(data: Omit<InsertionModel<ICalendarEvent>, 'notificationSent'>): Promise<ICalendarEvent['_id']>; + get(eventId: ICalendarEvent['_id']): Promise<ICalendarEvent | null>; + list(uid: IUser['_id'], date: Date): Promise<ICalendarEvent[]>; + update(eventId: ICalendarEvent['_id'], data: Partial<ICalendarEvent>): Promise<UpdateResult>; + delete(eventId: ICalendarEvent['_id']): Promise<DeleteResult>; + findImportedEvent(externalId: Required<ICalendarEvent>['externalId'], uid: ICalendarEvent['uid']): Promise<ICalendarEvent | null>; + parseDescriptionForMeetingUrl(description: string): Promise<string | undefined>; + setupNextNotification(): Promise<void>; +} diff --git a/packages/core-typings/src/ICalendarEvent.ts b/packages/core-typings/src/ICalendarEvent.ts new file mode 100644 index 000000000000..6bb0a7bb58e3 --- /dev/null +++ b/packages/core-typings/src/ICalendarEvent.ts @@ -0,0 +1,16 @@ +import type { IRocketChatRecord } from './IRocketChatRecord'; +import type { IUser } from './IUser'; + +export interface ICalendarEvent extends IRocketChatRecord { + startTime: Date; + uid: IUser['_id']; + subject: string; + description: string; + notificationSent: boolean; + + externalId?: string; + meetingUrl?: string; + + reminderMinutesBeforeStart?: number; + reminderTime?: Date; +} diff --git a/packages/core-typings/src/INotification.ts b/packages/core-typings/src/INotification.ts index a5fc9155eba8..46c3341eea23 100644 --- a/packages/core-typings/src/INotification.ts +++ b/packages/core-typings/src/INotification.ts @@ -1,3 +1,4 @@ +import type { ICalendarEvent } from './ICalendarEvent'; import type { IMessage } from './IMessage'; import type { IRoom } from './IRoom'; @@ -64,3 +65,11 @@ export interface INotificationDesktop { }; }; } + +export interface ICalendarNotification { + title: string; + text: string; + payload: { + _id: ICalendarEvent['_id']; + }; +} diff --git a/packages/core-typings/src/index.ts b/packages/core-typings/src/index.ts index 9ba91eb373b4..8cd004dd09f1 100644 --- a/packages/core-typings/src/index.ts +++ b/packages/core-typings/src/index.ts @@ -122,6 +122,7 @@ export * from './VideoConferenceCapabilities'; export * from './VideoConferenceOptions'; export * from './SpotlightUser'; +export * from './ICalendarEvent'; export * from './search'; export * from './omnichannel'; diff --git a/packages/cron/src/index.ts b/packages/cron/src/index.ts index dc6772a60407..8158971219ea 100644 --- a/packages/cron/src/index.ts +++ b/packages/cron/src/index.ts @@ -36,8 +36,22 @@ const runCronJobFunctionAndPersistResult = async (fn: () => Promise<any>, jobNam } }; +type ReservedJob = { + name: string; + callback: () => any | Promise<any>; +} & ( + | { + schedule: string; + timestamped: false; + } + | { + when: Date; + timestamped: true; + } +); + export class AgendaCronJobs { - private reservedJobs: { name: string; schedule: string; callback: () => any | Promise<any> }[] = []; + private reservedJobs: ReservedJob[] = []; private scheduler: Agenda | undefined; @@ -53,8 +67,12 @@ export class AgendaCronJobs { }); await this.scheduler.start(); - for await (const { name, schedule, callback } of this.reservedJobs) { - await this.add(name, schedule, callback); + for await (const job of this.reservedJobs) { + if (job.timestamped) { + await this.addAtTimestamp(job.name, job.when, job.callback); + } else { + await this.add(job.name, job.schedule, job.callback); + } } this.reservedJobs = []; @@ -62,15 +80,22 @@ export class AgendaCronJobs { public async add(name: string, schedule: string, callback: () => any | Promise<any>): Promise<void> { if (!this.scheduler) { - return this.reserve(name, schedule, callback); + return this.reserve({ name, schedule, callback, timestamped: false }); } - this.scheduler.define(name, async () => { - await runCronJobFunctionAndPersistResult(async () => callback(), name); - }); + await this.define(name, callback); await this.scheduler.every(schedule, name, {}, {}); } + public async addAtTimestamp(name: string, when: Date, callback: () => any | Promise<any>): Promise<void> { + if (!this.scheduler) { + return this.reserve({ name, when, callback, timestamped: true }); + } + + await this.define(name, callback); + await this.scheduler.schedule(when, name, {}); + } + public async remove(name: string): Promise<void> { if (!this.scheduler) { return this.unreserve(name); @@ -87,13 +112,23 @@ export class AgendaCronJobs { return this.scheduler.has({ name: jobName }); } - private async reserve(name: string, schedule: string, callback: () => any | Promise<any>): Promise<void> { - this.reservedJobs = [...this.reservedJobs, { name, schedule, callback }]; + private async reserve(config: ReservedJob): Promise<void> { + this.reservedJobs = [...this.reservedJobs, config]; } private async unreserve(jobName: string): Promise<void> { this.reservedJobs = this.reservedJobs.filter(({ name }) => name !== jobName); } + + private async define(jobName: string, callback: () => any | Promise<any>): Promise<void> { + if (!this.scheduler) { + throw new Error('Scheduler is not running.'); + } + + this.scheduler.define(jobName, async () => { + await runCronJobFunctionAndPersistResult(async () => callback(), jobName); + }); + } } export const cronJobs = new AgendaCronJobs(); diff --git a/packages/model-typings/src/index.ts b/packages/model-typings/src/index.ts index 8eff5184d3d7..eda84bd5a359 100644 --- a/packages/model-typings/src/index.ts +++ b/packages/model-typings/src/index.ts @@ -67,6 +67,7 @@ export * from './models/IVoipRoomModel'; export * from './models/IWebdavAccountsModel'; export * from './models/IMatrixBridgeRoomModel'; export * from './models/IMatrixBridgeUserModel'; +export * from './models/ICalendarEventModel'; export * from './models/IOmnichannelServiceLevelAgreementsModel'; export * from './models/IAppLogsModel'; export * from './models/IAppsModel'; diff --git a/packages/model-typings/src/models/ICalendarEventModel.ts b/packages/model-typings/src/models/ICalendarEventModel.ts new file mode 100644 index 000000000000..5f1611476602 --- /dev/null +++ b/packages/model-typings/src/models/ICalendarEventModel.ts @@ -0,0 +1,16 @@ +import type { FindCursor, UpdateResult } from 'mongodb'; +import type { ICalendarEvent, IUser } from '@rocket.chat/core-typings'; + +import type { IBaseModel } from './IBaseModel'; + +export interface ICalendarEventModel extends IBaseModel<ICalendarEvent> { + findByUserIdAndDate(uid: IUser['_id'], date: Date): FindCursor<ICalendarEvent>; + updateEvent(eventId: ICalendarEvent['_id'], eventData: Partial<ICalendarEvent>): Promise<UpdateResult>; + findNextNotificationDate(): Promise<Date | null>; + findEventsToNotify(notificationTime: Date, minutes: number): FindCursor<ICalendarEvent>; + flagNotificationSent(eventId: ICalendarEvent['_id']): Promise<UpdateResult>; + findOneByExternalIdAndUserId( + externalId: Required<ICalendarEvent>['externalId'], + uid: ICalendarEvent['uid'], + ): Promise<ICalendarEvent | null>; +} diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 03820ec7e39b..fd6a95979673 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -66,6 +66,7 @@ import type { IWebdavAccountsModel, IMatrixBridgedRoomModel, IMatrixBridgedUserModel, + ICalendarEventModel, IOmnichannelServiceLevelAgreementsModel, IAppsModel, IAppsPersistenceModel, @@ -163,6 +164,7 @@ export const VoipRoom = proxify<IVoipRoomModel>('IVoipRoomModel'); export const WebdavAccounts = proxify<IWebdavAccountsModel>('IWebdavAccountsModel'); export const MatrixBridgedRoom = proxify<IMatrixBridgedRoomModel>('IMatrixBridgedRoomModel'); export const MatrixBridgedUser = proxify<IMatrixBridgedUserModel>('IMatrixBridgedUserModel'); +export const CalendarEvent = proxify<ICalendarEventModel>('ICalendarEventModel'); export const OmnichannelServiceLevelAgreements = proxify<IOmnichannelServiceLevelAgreementsModel>( 'IOmnichannelServiceLevelAgreementsModel', ); diff --git a/packages/rest-typings/src/index.ts b/packages/rest-typings/src/index.ts index 44c62e12bdac..8266e5db6770 100644 --- a/packages/rest-typings/src/index.ts +++ b/packages/rest-typings/src/index.ts @@ -43,6 +43,7 @@ import type { CommandsEndpoints } from './v1/commands'; import type { MeEndpoints } from './v1/me'; import type { SubscriptionsEndpoints } from './v1/subscriptionsEndpoints'; import type { ImportEndpoints } from './v1/import'; +import type { CalendarEndpoints } from './v1/calendar'; import type { FederationEndpoints } from './v1/federation'; import type { ModerationEndpoints } from './v1/moderation'; import type { AuthEndpoints } from './v1/auth'; @@ -93,7 +94,9 @@ export interface Endpoints OAuthAppsEndpoint, SubscriptionsEndpoints, AutoTranslateEndpoints, + ImportEndpoints, FederationEndpoints, + CalendarEndpoints, AuthEndpoints, ImportEndpoints, DefaultEndpoints {} @@ -257,6 +260,7 @@ export * from './v1/e2e/e2eUpdateGroupKeyParamsPOST'; export * from './v1/import'; export * from './v1/voip'; export * from './v1/email-inbox'; +export * from './v1/calendar'; export * from './v1/federation'; export * from './v1/rooms'; export * from './v1/groups'; diff --git a/packages/rest-typings/src/v1/calendar/CalendarEventCreateProps.ts b/packages/rest-typings/src/v1/calendar/CalendarEventCreateProps.ts new file mode 100644 index 000000000000..5358510c97ab --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/CalendarEventCreateProps.ts @@ -0,0 +1,47 @@ +import type { JSONSchemaType } from 'ajv'; +import Ajv from 'ajv'; + +const ajv = new Ajv(); + +export type CalendarEventCreateProps = { + startTime: string; + externalId?: string; + subject: string; + description: string; + meetingUrl?: string; + reminderMinutesBeforeStart?: number; +}; + +const calendarEventCreatePropsSchema: JSONSchemaType<CalendarEventCreateProps> = { + type: 'object', + properties: { + startTime: { + type: 'string', + nullable: false, + }, + externalId: { + type: 'string', + nullable: true, + }, + subject: { + type: 'string', + nullable: false, + }, + description: { + type: 'string', + nullable: false, + }, + meetingUrl: { + type: 'string', + nullable: true, + }, + reminderMinutesBeforeStart: { + type: 'number', + nullable: true, + }, + }, + required: ['startTime', 'subject', 'description'], + additionalProperties: false, +}; + +export const isCalendarEventCreateProps = ajv.compile(calendarEventCreatePropsSchema); diff --git a/packages/rest-typings/src/v1/calendar/CalendarEventDeleteProps.ts b/packages/rest-typings/src/v1/calendar/CalendarEventDeleteProps.ts new file mode 100644 index 000000000000..17d17bb8c441 --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/CalendarEventDeleteProps.ts @@ -0,0 +1,22 @@ +import type { ICalendarEvent } from '@rocket.chat/core-typings'; +import type { JSONSchemaType } from 'ajv'; +import Ajv from 'ajv'; + +const ajv = new Ajv(); + +export type CalendarEventDeleteProps = { + eventId: ICalendarEvent['_id']; +}; + +const calendarEventDeletePropsSchema: JSONSchemaType<CalendarEventDeleteProps> = { + type: 'object', + properties: { + eventId: { + type: 'string', + }, + }, + required: ['eventId'], + additionalProperties: false, +}; + +export const isCalendarEventDeleteProps = ajv.compile(calendarEventDeletePropsSchema); diff --git a/packages/rest-typings/src/v1/calendar/CalendarEventImportProps.ts b/packages/rest-typings/src/v1/calendar/CalendarEventImportProps.ts new file mode 100644 index 000000000000..955184a36115 --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/CalendarEventImportProps.ts @@ -0,0 +1,47 @@ +import type { JSONSchemaType } from 'ajv'; +import Ajv from 'ajv'; + +const ajv = new Ajv(); + +export type CalendarEventImportProps = { + startTime: string; + externalId: string; + subject: string; + description: string; + meetingUrl?: string; + reminderMinutesBeforeStart?: number; +}; + +const calendarEventImportPropsSchema: JSONSchemaType<CalendarEventImportProps> = { + type: 'object', + properties: { + startTime: { + type: 'string', + nullable: false, + }, + externalId: { + type: 'string', + nullable: false, + }, + subject: { + type: 'string', + nullable: false, + }, + description: { + type: 'string', + nullable: false, + }, + meetingUrl: { + type: 'string', + nullable: true, + }, + reminderMinutesBeforeStart: { + type: 'number', + nullable: true, + }, + }, + required: ['startTime', 'externalId', 'subject', 'description'], + additionalProperties: false, +}; + +export const isCalendarEventImportProps = ajv.compile(calendarEventImportPropsSchema); diff --git a/packages/rest-typings/src/v1/calendar/CalendarEventInfoProps.ts b/packages/rest-typings/src/v1/calendar/CalendarEventInfoProps.ts new file mode 100644 index 000000000000..c5e00d537431 --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/CalendarEventInfoProps.ts @@ -0,0 +1,22 @@ +import Ajv from 'ajv'; +import type { JSONSchemaType } from 'ajv'; + +const ajv = new Ajv({ + coerceTypes: true, +}); + +export type CalendarEventInfoProps = { id: string }; + +const calendarEventInfoPropsSchema: JSONSchemaType<CalendarEventInfoProps> = { + type: 'object', + properties: { + id: { + type: 'string', + nullable: false, + }, + }, + required: ['id'], + additionalProperties: false, +}; + +export const isCalendarEventInfoProps = ajv.compile(calendarEventInfoPropsSchema); diff --git a/packages/rest-typings/src/v1/calendar/CalendarEventListProps.ts b/packages/rest-typings/src/v1/calendar/CalendarEventListProps.ts new file mode 100644 index 000000000000..e035d4faa599 --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/CalendarEventListProps.ts @@ -0,0 +1,22 @@ +import Ajv from 'ajv'; +import type { JSONSchemaType } from 'ajv'; + +const ajv = new Ajv({ + coerceTypes: true, +}); + +export type CalendarEventListProps = { date: string }; + +const calendarEventListPropsSchema: JSONSchemaType<CalendarEventListProps> = { + type: 'object', + properties: { + date: { + type: 'string', + nullable: false, + }, + }, + required: ['date'], + additionalProperties: false, +}; + +export const isCalendarEventListProps = ajv.compile(calendarEventListPropsSchema); diff --git a/packages/rest-typings/src/v1/calendar/CalendarEventUpdateProps.ts b/packages/rest-typings/src/v1/calendar/CalendarEventUpdateProps.ts new file mode 100644 index 000000000000..1004cd09990e --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/CalendarEventUpdateProps.ts @@ -0,0 +1,48 @@ +import type { ICalendarEvent } from '@rocket.chat/core-typings'; +import type { JSONSchemaType } from 'ajv'; +import Ajv from 'ajv'; + +const ajv = new Ajv(); + +export type CalendarEventUpdateProps = { + eventId: ICalendarEvent['_id']; + startTime: string; + subject: string; + description: string; + meetingUrl?: string; + reminderMinutesBeforeStart?: number; +}; + +const calendarEventUpdatePropsSchema: JSONSchemaType<CalendarEventUpdateProps> = { + type: 'object', + properties: { + eventId: { + type: 'string', + nullable: false, + }, + startTime: { + type: 'string', + nullable: false, + }, + subject: { + type: 'string', + nullable: false, + }, + description: { + type: 'string', + nullable: false, + }, + meetingUrl: { + type: 'string', + nullable: true, + }, + reminderMinutesBeforeStart: { + type: 'number', + nullable: true, + }, + }, + required: ['eventId', 'startTime', 'subject', 'description'], + additionalProperties: false, +}; + +export const isCalendarEventUpdateProps = ajv.compile(calendarEventUpdatePropsSchema); diff --git a/packages/rest-typings/src/v1/calendar/index.ts b/packages/rest-typings/src/v1/calendar/index.ts new file mode 100644 index 000000000000..de00bd3d632e --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/index.ts @@ -0,0 +1,41 @@ +import type { ICalendarEvent } from '@rocket.chat/core-typings'; + +import type { CalendarEventCreateProps } from './CalendarEventCreateProps'; +import type { CalendarEventListProps } from './CalendarEventListProps'; +import type { CalendarEventImportProps } from './CalendarEventImportProps'; +import type { CalendarEventInfoProps } from './CalendarEventInfoProps'; +import type { CalendarEventUpdateProps } from './CalendarEventUpdateProps'; +import type { CalendarEventDeleteProps } from './CalendarEventDeleteProps'; + +export * from './CalendarEventCreateProps'; +export * from './CalendarEventDeleteProps'; +export * from './CalendarEventImportProps'; +export * from './CalendarEventInfoProps'; +export * from './CalendarEventUpdateProps'; +export * from './CalendarEventListProps'; + +export type CalendarEndpoints = { + '/v1/calendar-events.create': { + POST: (params: CalendarEventCreateProps) => { id: ICalendarEvent['_id'] }; + }; + + '/v1/calendar-events.list': { + GET: (params: CalendarEventListProps) => { data: ICalendarEvent[] }; + }; + + '/v1/calendar-events.info': { + GET: (params: CalendarEventInfoProps) => { event: ICalendarEvent }; + }; + + '/v1/calendar-events.import': { + POST: (params: CalendarEventImportProps) => { id: ICalendarEvent['_id'] }; + }; + + '/v1/calendar-events.update': { + POST: (params: CalendarEventUpdateProps) => void; + }; + + '/v1/calendar-events.delete': { + POST: (params: CalendarEventDeleteProps) => void; + }; +}; diff --git a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts index 69ac5440189a..d820dfe5cb2d 100644 --- a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts +++ b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts @@ -41,6 +41,7 @@ export type UsersSetPreferencesParamsPOST = { dontAskAgainList?: Array<{ action: string; label: string }>; themeAppearence?: 'auto' | 'light' | 'dark'; receiveLoginDetectionEmail?: boolean; + notifyCalendarEvents?: boolean; idleTimeLimit?: number; omnichannelTranscriptEmail?: boolean; omnichannelTranscriptPDF?: boolean; @@ -204,6 +205,10 @@ const UsersSetPreferencesParamsPostSchema = { type: 'boolean', nullable: true, }, + notifyCalendarEvents: { + type: 'boolean', + nullable: true, + }, idleTimeLimit: { type: 'number', nullable: true, diff --git a/packages/ui-contexts/src/ServerContext/streams.ts b/packages/ui-contexts/src/ServerContext/streams.ts index 6165e15708d6..5892371afb1d 100644 --- a/packages/ui-contexts/src/ServerContext/streams.ts +++ b/packages/ui-contexts/src/ServerContext/streams.ts @@ -17,6 +17,7 @@ import type { IOmnichannelCannedResponse, IIntegrationHistory, IUserDataEvent, + ICalendarNotification, IUserStatus, ILivechatInquiryRecord, } from '@rocket.chat/core-typings'; @@ -154,6 +155,7 @@ export interface StreamerEvents { }, ]; }, + { key: `${string}/calendar`; args: [ICalendarNotification] }, ]; 'importers': [ From 18c55b0bbe9df3cac53248dc0e05b173f040bece Mon Sep 17 00:00:00 2001 From: XpertWebApp <68045060+XpertWebApp@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:15:16 +0530 Subject: [PATCH 054/149] fix: sound status goes out of window on popout mode (#28181) --- packages/livechat/src/components/Screen/Header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/livechat/src/components/Screen/Header.js b/packages/livechat/src/components/Screen/Header.js index 793a5a37f6dc..9f9e41810c7b 100644 --- a/packages/livechat/src/components/Screen/Header.js +++ b/packages/livechat/src/components/Screen/Header.js @@ -78,7 +78,7 @@ class ScreenHeader extends Component { </Header.Content> <Tooltip.Container> <Header.Actions> - <Tooltip.Trigger content={notificationsEnabled ? t('sound_is_on') : t('sound_is_off')}> + <Tooltip.Trigger content={notificationsEnabled ? t('sound_is_on') : t('sound_is_off')} placement='bottom-left'> <Header.Action aria-label={notificationsEnabled ? t('disable_notifications') : t('enable_notifications')} onClick={notificationsEnabled ? onDisableNotifications : onEnableNotifications} From 706561aa2731c2bd77dfba95891fa0094d3a235c Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Tue, 4 Jul 2023 17:54:45 -0300 Subject: [PATCH 055/149] chore: throttle exceptions counter increment (#29719) --- .../server/lib/RocketChat.ErrorHandler.ts | 7 ++++++- apps/meteor/app/lib/server/lib/meteorFixes.js | 10 ++++++++-- apps/meteor/ee/server/startup/seatsCap.ts | 5 +++-- apps/meteor/lib/utils/throttledCounter.ts | 20 +++++++++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 apps/meteor/lib/utils/throttledCounter.ts diff --git a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts index 9a4fc28b8f3e..0aa8c3a53747 100644 --- a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts +++ b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts @@ -3,6 +3,11 @@ import { Settings, Users, Rooms } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { sendMessage } from '../../../lib/server'; +import { throttledCounter } from '../../../../lib/utils/throttledCounter'; + +const incException = throttledCounter((counter) => { + Settings.incrementValueById('Uncaught_Exceptions_Count', counter).catch(console.error); +}, 10000); class ErrorHandler { reporting: boolean; @@ -40,7 +45,7 @@ class ErrorHandler { async registerHandlers() { process.on('uncaughtException', async (error) => { - await Settings.incrementValueById('Uncaught_Exceptions_Count'); + incException(); if (!this.reporting) { return; } diff --git a/apps/meteor/app/lib/server/lib/meteorFixes.js b/apps/meteor/app/lib/server/lib/meteorFixes.js index 6b88febda2aa..39616cac4042 100644 --- a/apps/meteor/app/lib/server/lib/meteorFixes.js +++ b/apps/meteor/app/lib/server/lib/meteorFixes.js @@ -1,6 +1,8 @@ import { MongoInternals } from 'meteor/mongo'; import { Settings } from '@rocket.chat/models'; +import { throttledCounter } from '../../../../lib/utils/throttledCounter'; + const timeoutQuery = parseInt(process.env.OBSERVERS_CHECK_TIMEOUT) || 2 * 60 * 1000; const interval = parseInt(process.env.OBSERVERS_CHECK_INTERVAL) || 60 * 1000; const debug = Boolean(process.env.OBSERVERS_CHECK_DEBUG); @@ -44,6 +46,10 @@ setInterval(() => { }); }, interval); +const incException = throttledCounter((counter) => { + Settings.incrementValueById('Uncaught_Exceptions_Count', counter).catch(console.error); +}, 10000); + /** * If some promise is rejected and doesn't have a catch (unhandledRejection) it may cause this finally * here https://github.com/meteor/meteor/blob/be6e529a739f47446950e045f4547ee60e5de7ae/packages/mongo/oplog_tailing.js#L348 @@ -58,8 +64,8 @@ setInterval(() => { * we will start respecting this and exit the process to prevent these kind of problems. */ -process.on('unhandledRejection', async (error) => { - await Settings.incrementValueById('Uncaught_Exceptions_Count'); +process.on('unhandledRejection', (error) => { + incException(); console.error('=== UnHandledPromiseRejection ==='); console.error(error); diff --git a/apps/meteor/ee/server/startup/seatsCap.ts b/apps/meteor/ee/server/startup/seatsCap.ts index 188c8dfe93b9..d5ec74919568 100644 --- a/apps/meteor/ee/server/startup/seatsCap.ts +++ b/apps/meteor/ee/server/startup/seatsCap.ts @@ -1,6 +1,7 @@ import { Meteor } from 'meteor/meteor'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { throttle } from 'underscore'; import { callbacks } from '../../../lib/callbacks'; import { canAddNewUser, getMaxActiveUsers, onValidateLicenses } from '../../app/license/server/license'; @@ -79,7 +80,7 @@ callbacks.add( 'check-max-user-seats', ); -async function handleMaxSeatsBanners() { +const handleMaxSeatsBanners = throttle(async function _handleMaxSeatsBanners() { const maxActiveUsers = getMaxActiveUsers(); if (!maxActiveUsers) { @@ -106,7 +107,7 @@ async function handleMaxSeatsBanners() { } else { await enableDangerBanner(); } -} +}, 10000); callbacks.add('afterCreateUser', handleMaxSeatsBanners, callbacks.priority.MEDIUM, 'handle-max-seats-banners'); diff --git a/apps/meteor/lib/utils/throttledCounter.ts b/apps/meteor/lib/utils/throttledCounter.ts new file mode 100644 index 000000000000..6580699b3630 --- /dev/null +++ b/apps/meteor/lib/utils/throttledCounter.ts @@ -0,0 +1,20 @@ +import { throttle } from 'underscore'; + +export function throttledCounter(fn: (counter: number) => unknown, wait: number) { + let counter = 0; + + const throttledFn = throttle( + () => { + fn(counter); + + counter = 0; + }, + wait, + { leading: false }, + ); + + return () => { + counter++; + throttledFn(); + }; +} From 4ffd7fb2092b6e099f1bf6e6414ca4a40111ef33 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Tue, 4 Jul 2023 18:21:00 -0300 Subject: [PATCH 056/149] chore: device management improve readability and performance (#29712) --- .../ee/server/lib/deviceManagement/session.ts | 160 +++++++++--------- .../ee/server/startup/deviceManagement.ts | 7 +- apps/meteor/server/models/raw/Users.js | 10 ++ .../model-typings/src/models/IUsersModel.ts | 1 + 4 files changed, 101 insertions(+), 77 deletions(-) diff --git a/apps/meteor/ee/server/lib/deviceManagement/session.ts b/apps/meteor/ee/server/lib/deviceManagement/session.ts index 22ded104cd4d..f7f67a1c93ca 100644 --- a/apps/meteor/ee/server/lib/deviceManagement/session.ts +++ b/apps/meteor/ee/server/lib/deviceManagement/session.ts @@ -1,14 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { UAParser } from 'ua-parser-js'; -import type { ISocketConnection, IUser } from '@rocket.chat/core-typings'; +import type { ISocketConnection } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import * as Mailer from '../../../../app/mailer/server/api'; import { settings } from '../../../../app/settings/server'; import { UAParserDesktop, UAParserMobile } from '../../../../app/statistics/server/lib/UAParserCustom'; import { deviceManagementEvents } from '../../../../server/services/device-management/events'; -import { hasLicense } from '../../../app/license/server/license'; import { getUserPreference } from '../../../../app/utils/server'; import { t } from '../../../../app/utils/lib/i18n'; @@ -23,88 +22,97 @@ Meteor.startup(() => { const uaParser = async ( uaString: ISocketConnection['httpHeaders']['user-agent'], ): Promise<UAParser.IResult & { app?: { name: string; version: string; bundle: string } }> => { - let rcAgent = {}; - if (uaString && UAParserMobile.isMobileApp(uaString)) { - rcAgent = UAParserMobile.uaObject(uaString); - } - - if (uaString && UAParserDesktop.isDesktopApp(uaString)) { - rcAgent = UAParserDesktop.uaObject(uaString); - } - const ua = new UAParser(uaString); - return { ...ua.getResult(), ...rcAgent }; + return { + ...ua.getResult(), + ...(uaString && UAParserMobile.isMobileApp(uaString) && UAParserMobile.uaObject(uaString)), + ...(uaString && UAParserDesktop.isDesktopApp(uaString) && UAParserDesktop.uaObject(uaString)), + }; }; -export const listenSessionLogin = async (): Promise<void> => { - deviceManagementEvents.on('device-login', async ({ userId, connection }) => { - if (!hasLicense('device-management')) return; +export const listenSessionLogin = () => { + return deviceManagementEvents.on('device-login', async ({ userId, connection }) => { + const deviceEnabled = settings.get('Device_Management_Enable_Login_Emails'); + + if (!deviceEnabled) { + return; + } + + if (connection.loginToken) { + return; + } + + const user = await Users.findOneByIdWithEmailAddress(userId, { + projection: { 'name': 1, 'username': 1, 'emails': 1, 'settings.preferences.receiveLoginDetectionEmail': 1 }, + }); + + if (!user?.emails?.length) { + return; + } + + const userReceiveLoginEmailPreference = + !settings.get('Device_Management_Allow_Login_Email_preference') || + (await getUserPreference(userId, 'receiveLoginDetectionEmail', true)); - const user = await Users.findOneById<IUser>(userId, { projection: { name: 1, username: 1, emails: 1 } }); - if (user?.emails?.length && !connection.loginToken) { - const { - name, - username, - emails: [{ address: email }], - } = user; - const { browser, os, device, cpu, app } = await uaParser(connection.httpHeaders['user-agent']); + if (!userReceiveLoginEmailPreference) { + return; + } - const mailData = { - name, - username, - browserInfo: `${browser.name} ${browser.version}`, - osInfo: `${os.name}`, - deviceInfo: `${device.type || t('Device_Management_Device_Unknown')} ${device.vendor || ''} ${device.model || ''} ${ - cpu.architecture || '' - }`, - ipInfo: connection.clientAddress, - userAgent: '', - }; + const { + name, + username, + emails: [{ address: email }], + } = user; + const { browser, os, device, cpu, app } = await uaParser(connection.httpHeaders['user-agent']); - switch (device.type) { - case 'mobile': - case 'tablet': - case 'smarttv': - mailData.browserInfo = `${browser.name} ${browser.version}`; - mailData.osInfo = `${os.name}`; - mailData.deviceInfo = `${device.type} ${device.vendor || ''} ${device.model || ''} ${cpu.architecture || ''}`; - break; - case 'mobile-app': - mailData.browserInfo = `Rocket.Chat App ${app?.bundle || app?.version}`; - mailData.osInfo = `${os.name}`; - mailData.deviceInfo = 'Mobile App'; - break; - case 'desktop-app': - mailData.browserInfo = `Rocket.Chat ${app?.name || browser.name} ${app?.bundle || app?.version || browser.version}`; - mailData.osInfo = `${os.name}`; - mailData.deviceInfo = `Desktop App ${cpu.architecture || ''}`; - break; - default: - mailData.userAgent = connection.httpHeaders['user-agent'] || ''; - break; - } + const mailData = { + name, + username, + browserInfo: `${browser.name} ${browser.version}`, + osInfo: `${os.name}`, + deviceInfo: `${device.type || t('Device_Management_Device_Unknown')} ${device.vendor || ''} ${device.model || ''} ${ + cpu.architecture || '' + }`, + ipInfo: connection.clientAddress, + userAgent: '', + }; - try { - const userReceiveLoginEmailPreference = settings.get('Device_Management_Allow_Login_Email_preference') - ? await getUserPreference(userId, 'receiveLoginDetectionEmail', true) - : true; - const shouldSendLoginEmail = settings.get('Device_Management_Enable_Login_Emails') && userReceiveLoginEmailPreference; + switch (device.type) { + case 'mobile': + case 'tablet': + case 'smarttv': + mailData.browserInfo = `${browser.name} ${browser.version}`; + mailData.osInfo = `${os.name}`; + mailData.deviceInfo = `${device.type} ${device.vendor || ''} ${device.model || ''} ${cpu.architecture || ''}`; + break; + case 'mobile-app': + mailData.browserInfo = `Rocket.Chat App ${app?.bundle || app?.version}`; + mailData.osInfo = `${os.name}`; + mailData.deviceInfo = 'Mobile App'; + break; + case 'desktop-app': + mailData.browserInfo = `Rocket.Chat ${app?.name || browser.name} ${app?.bundle || app?.version || browser.version}`; + mailData.osInfo = `${os.name}`; + mailData.deviceInfo = `Desktop App ${cpu.architecture || ''}`; + break; + default: + mailData.userAgent = connection.httpHeaders['user-agent'] || ''; + break; + } - if (shouldSendLoginEmail) { - await Mailer.send({ - to: `${name} <${email}>`, - from: Accounts.emailTemplates.from, - subject: settings.get('Device_Management_Email_Subject'), - html: mailTemplates, - data: mailData, - }); - } - } catch ({ message }: any) { - throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${message}`, { - method: 'listenSessionLogin', - message, - }); - } + try { + await Mailer.send({ + to: `${name} <${email}>`, + from: Accounts.emailTemplates.from, + subject: settings.get('Device_Management_Email_Subject'), + html: mailTemplates, + data: mailData, + }); + } catch ({ message }: any) { + throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${message}`, { + method: 'listenSessionLogin', + message, + }); } }); }; diff --git a/apps/meteor/ee/server/startup/deviceManagement.ts b/apps/meteor/ee/server/startup/deviceManagement.ts index 6e9ba8540688..a9a1c805f72d 100644 --- a/apps/meteor/ee/server/startup/deviceManagement.ts +++ b/apps/meteor/ee/server/startup/deviceManagement.ts @@ -1,6 +1,7 @@ import { onToggledFeature } from '../../app/license/server/license'; import { addSettings } from '../settings/deviceManagement'; +let stopListening: (() => void) | undefined; onToggledFeature('device-management', { up: async () => { const { createPermissions, createEmailTemplates } = await import('../lib/deviceManagement/startup'); @@ -9,6 +10,10 @@ onToggledFeature('device-management', { await addSettings(); await createPermissions(); await createEmailTemplates(); - await listenSessionLogin(); + stopListening = await listenSessionLogin(); + }, + down: async () => { + stopListening?.(); + stopListening = undefined; }, }); diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 0cb88a178ccc..996c3abaebcd 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1250,6 +1250,16 @@ export class UsersRaw extends BaseRaw { return this.findOne({ 'services.password.reset.token': token }, options); } + findOneByIdWithEmailAddress(userId, options) { + return this.findOne( + { + _id: userId, + emails: { $exists: true, $ne: [] }, + }, + options, + ); + } + setFederationAvatarUrlById(userId, federationAvatarUrl) { return this.updateOne( { diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 5900b5a14091..70b161acb6f3 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -17,6 +17,7 @@ export interface IUsersModel extends IBaseModel<IUser> { addRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][]): Promise<UpdateResult>; findUsersInRoles<T = IUser>(roles: IRole['_id'][], scope?: null, options?: any): FindCursor<T>; findPaginatedUsersInRoles<T = IUser>(roles: IRole['_id'][], options?: any): FindPaginated<FindCursor<T>>; + findOneByIdWithEmailAddress(uid: IUser['_id'], options?: FindOptions<IUser>): Promise<IUser | null>; findOneByUsername<T = IUser>(username: string, options?: any): Promise<T>; findOneAgentById<T = ILivechatAgent>(_id: string, options: any): Promise<T>; findUsersInRolesWithQuery<T = IUser>(roles: IRole['_id'] | IRole['_id'][], query: any, options: any): FindCursor<T>; From c7ff0557bc91a12ace327f4d13cbab837bc615dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Tue, 4 Jul 2023 18:52:53 -0300 Subject: [PATCH 057/149] chore: `MenuV2` on `UserDropdown` (#29672) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- .../{ => GenericMenu}/GenericMenu.tsx | 34 +++- .../{ => GenericMenu}/GenericMenuItem.tsx | 10 +- .../GenericMenu/hooks/useHandleMenuAction.tsx | 13 ++ .../OmnichannelSortingDisclaimer.tsx | 24 +-- .../client/hooks/useHandleMenuAction.tsx | 10 - .../sidebar/header/UserAvatarButton.tsx | 78 -------- .../sidebar/header/UserAvatarWithStatus.tsx | 57 ++++++ .../client/sidebar/header/UserDropdown.tsx | 183 ------------------ .../meteor/client/sidebar/header/UserMenu.tsx | 31 +++ .../client/sidebar/header/UserMenuHeader.tsx | 43 ++++ .../sidebar/header/actions/Administration.tsx | 9 +- .../sidebar/header/actions/CreateRoom.tsx | 18 +- .../client/sidebar/header/actions/Sort.tsx | 2 +- .../actions/hooks/useAdministrationItems.tsx | 2 +- .../header/actions/hooks/useAppsItems.tsx | 2 +- .../header/actions/hooks/useAuditItems.tsx | 2 +- .../actions/hooks/useCreateRoomItems.tsx | 2 +- .../actions/hooks/useGroupingListItems.tsx | 2 +- .../hooks/useMatrixFederationItems.tsx.tsx | 2 +- .../header/actions/hooks/useSortModeItems.tsx | 26 +-- .../header/actions/hooks/useViewModeItems.tsx | 2 +- .../sidebar/header/hooks/useAccountItems.tsx | 33 ++++ .../hooks/useCustomStatusModalHandler.tsx | 14 ++ .../sidebar/header/hooks/useStatusItems.tsx | 77 ++++++++ .../sidebar/header/hooks/useThemeItems.tsx | 33 ++++ .../sidebar/header/hooks/useUserMenu.tsx | 31 +++ apps/meteor/client/sidebar/header/index.tsx | 5 +- ...nichannel-auto-onhold-chat-closing.spec.ts | 2 +- ...nel-changing-room-priority-and-sla.spec.ts | 2 +- .../omnichannel/omnichannel-takeChat.spec.ts | 4 +- ...channel-transfer-to-another-agents.spec.ts | 8 +- .../page-objects/fragments/home-sidenav.ts | 4 +- 32 files changed, 413 insertions(+), 352 deletions(-) rename apps/meteor/client/components/{ => GenericMenu}/GenericMenu.tsx (53%) rename apps/meteor/client/components/{ => GenericMenu}/GenericMenuItem.tsx (57%) create mode 100644 apps/meteor/client/components/GenericMenu/hooks/useHandleMenuAction.tsx delete mode 100644 apps/meteor/client/hooks/useHandleMenuAction.tsx delete mode 100644 apps/meteor/client/sidebar/header/UserAvatarButton.tsx create mode 100644 apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx delete mode 100644 apps/meteor/client/sidebar/header/UserDropdown.tsx create mode 100644 apps/meteor/client/sidebar/header/UserMenu.tsx create mode 100644 apps/meteor/client/sidebar/header/UserMenuHeader.tsx create mode 100644 apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx create mode 100644 apps/meteor/client/sidebar/header/hooks/useCustomStatusModalHandler.tsx create mode 100644 apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx create mode 100644 apps/meteor/client/sidebar/header/hooks/useThemeItems.tsx create mode 100644 apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx diff --git a/apps/meteor/client/components/GenericMenu.tsx b/apps/meteor/client/components/GenericMenu/GenericMenu.tsx similarity index 53% rename from apps/meteor/client/components/GenericMenu.tsx rename to apps/meteor/client/components/GenericMenu/GenericMenu.tsx index 21a413a86644..5a9ad9a0877c 100644 --- a/apps/meteor/client/components/GenericMenu.tsx +++ b/apps/meteor/client/components/GenericMenu/GenericMenu.tsx @@ -1,20 +1,21 @@ -import type { Icon } from '@rocket.chat/fuselage'; +import type { IconButton } from '@rocket.chat/fuselage'; import { MenuItem, MenuSection, MenuV2 } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; +import type { ComponentProps, ReactNode } from 'react'; import React from 'react'; import type { GenericMenuItemProps } from './GenericMenuItem'; import GenericMenuItem from './GenericMenuItem'; +import { useHandleMenuAction } from './hooks/useHandleMenuAction'; type GenericMenuCommonProps = { - icon?: ComponentProps<typeof Icon>['name']; + icon?: ComponentProps<typeof IconButton>['icon']; title: string; }; type GenericMenuConditionalProps = | { sections?: { - title?: string; + title?: ReactNode; items: GenericMenuItemProps[]; permission?: boolean | '' | 0 | null | undefined; }[]; @@ -27,18 +28,29 @@ type GenericMenuConditionalProps = type GenericMenuProps = GenericMenuCommonProps & GenericMenuConditionalProps & Omit<ComponentProps<typeof MenuV2>, 'children'>; -const GenericMenu = ({ title, icon = 'menu', ...props }: GenericMenuProps) => { +const GenericMenu = ({ title, icon = 'menu', onAction, ...props }: GenericMenuProps) => { const t = useTranslation(); const sections = 'sections' in props && props.sections; const items = 'items' in props && props.items; + const itemsList = sections ? sections.reduce((acc, { items }) => [...acc, ...items], [] as GenericMenuItemProps[]) : items || []; + + const disabledKeys = itemsList.filter(({ disabled }) => disabled).map(({ id }) => id); + const handleAction = useHandleMenuAction(itemsList || []); + return ( <> {sections && ( - <MenuV2 icon={icon} title={t.has(title) ? t(title) : title} {...props}> + <MenuV2 + icon={icon} + title={t.has(title) ? t(title) : title} + onAction={onAction || handleAction} + {...(disabledKeys && { disabledKeys })} + {...props} + > {sections.map(({ title, items }, key) => ( - <MenuSection title={title && (t.has(title) ? t(title) : title)} items={items} key={`${title}-${key}`}> + <MenuSection title={typeof title === 'string' && t.has(title) ? t(title) : title} items={items} key={`${title}-${key}`}> {(item) => ( <MenuItem key={item.id}> <GenericMenuItem {...item} /> @@ -49,7 +61,13 @@ const GenericMenu = ({ title, icon = 'menu', ...props }: GenericMenuProps) => { </MenuV2> )} {items && ( - <MenuV2 icon={icon} title={t.has(title) ? t(title) : title} {...props}> + <MenuV2 + icon={icon} + title={t.has(title) ? t(title) : title} + onAction={onAction || handleAction} + {...(disabledKeys && { disabledKeys })} + {...props} + > {items.map((item) => ( <MenuItem key={item.id}> <GenericMenuItem {...item} /> diff --git a/apps/meteor/client/components/GenericMenuItem.tsx b/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx similarity index 57% rename from apps/meteor/client/components/GenericMenuItem.tsx rename to apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx index ad546e0473ef..ca56b79de258 100644 --- a/apps/meteor/client/components/GenericMenuItem.tsx +++ b/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx @@ -1,18 +1,22 @@ -import { MenuItemContent, MenuItemIcon, MenuItemInput } from '@rocket.chat/fuselage'; +import { MenuItemColumn, MenuItemContent, MenuItemIcon, MenuItemInput } from '@rocket.chat/fuselage'; import type { ComponentProps, ReactNode } from 'react'; import React from 'react'; export type GenericMenuItemProps = { - id?: string; + id: string; icon?: ComponentProps<typeof MenuItemIcon>['name']; content?: ReactNode; addon?: ReactNode; onClick?: () => void; + status?: ReactNode; + disabled?: boolean; + description?: ReactNode; }; -const GenericMenuItem = ({ icon, content, addon }: GenericMenuItemProps) => ( +const GenericMenuItem = ({ icon, content, addon, status }: GenericMenuItemProps) => ( <> {icon && <MenuItemIcon name={icon} />} + {status && <MenuItemColumn>{status}</MenuItemColumn>} {content && <MenuItemContent>{content}</MenuItemContent>} {addon && <MenuItemInput>{addon}</MenuItemInput>} </> diff --git a/apps/meteor/client/components/GenericMenu/hooks/useHandleMenuAction.tsx b/apps/meteor/client/components/GenericMenu/hooks/useHandleMenuAction.tsx new file mode 100644 index 000000000000..c67c8acd3c5e --- /dev/null +++ b/apps/meteor/client/components/GenericMenu/hooks/useHandleMenuAction.tsx @@ -0,0 +1,13 @@ +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import type { GenericMenuItemProps } from '../GenericMenuItem'; + +export const useHandleMenuAction = (items: GenericMenuItemProps[], close?: () => void) => { + return useMutableCallback((id) => { + const item = items.find((item) => item.id === id && !!item.onClick); + if (item) { + item.onClick?.(); + close?.(); + } + }); +}; diff --git a/apps/meteor/client/components/Omnichannel/OmnichannelSortingDisclaimer.tsx b/apps/meteor/client/components/Omnichannel/OmnichannelSortingDisclaimer.tsx index 11c5f5ec8ab9..89e1a508b368 100644 --- a/apps/meteor/client/components/Omnichannel/OmnichannelSortingDisclaimer.tsx +++ b/apps/meteor/client/components/Omnichannel/OmnichannelSortingDisclaimer.tsx @@ -1,14 +1,12 @@ import { OmnichannelSortingMechanismSettingType as OmniSortingType } from '@rocket.chat/core-typings'; -import { Box } from '@rocket.chat/fuselage'; import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useState } from 'react'; -type OmnichannelSortingDisclaimerProps = { - id?: string; -}; +import { useOmnichannelEnterpriseEnabled } from '../../hooks/omnichannel/useOmnichannelEnterpriseEnabled'; + +export const useOmnichannelSortingDisclaimer = () => { + const isOmnichannelEnabled = useOmnichannelEnterpriseEnabled(); -export const OmnichannelSortingDisclaimer = (props: OmnichannelSortingDisclaimerProps) => { - const t = useTranslation(); const sortingMechanism = useSetting<OmniSortingType>('Omnichannel_sorting_mechanism') || OmniSortingType.Timestamp; const [{ [sortingMechanism]: type }] = useState({ @@ -17,13 +15,17 @@ export const OmnichannelSortingDisclaimer = (props: OmnichannelSortingDisclaimer [OmniSortingType.Timestamp]: '', } as const); + return isOmnichannelEnabled ? type : ''; +}; + +export const OmnichannelSortingDisclaimer = () => { + const t = useTranslation(); + + const type = useOmnichannelSortingDisclaimer(); + if (!type) { return null; } - return ( - <Box is='small' display='block' padding='4px 12px' fontSize='14px' lineHeight='20px' maxWidth='180px' {...props}> - {t('Omnichannel_sorting_disclaimer', { sortingMechanism: t(type) })} - </Box> - ); + return <>{t('Omnichannel_sorting_disclaimer', { sortingMechanism: t(type) })}</>; }; diff --git a/apps/meteor/client/hooks/useHandleMenuAction.tsx b/apps/meteor/client/hooks/useHandleMenuAction.tsx deleted file mode 100644 index f799ab464e7b..000000000000 --- a/apps/meteor/client/hooks/useHandleMenuAction.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; - -import type { GenericMenuItemProps } from '../components/GenericMenuItem'; - -export const useHandleMenuAction = (items: GenericMenuItemProps[]) => { - return useMutableCallback((id) => { - const item = items.find((item) => item.id === id); - item?.onClick && item.onClick(); - }); -}; diff --git a/apps/meteor/client/sidebar/header/UserAvatarButton.tsx b/apps/meteor/client/sidebar/header/UserAvatarButton.tsx deleted file mode 100644 index e22b7c8deca0..000000000000 --- a/apps/meteor/client/sidebar/header/UserAvatarButton.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { css } from '@rocket.chat/css-in-js'; -import { Box, Dropdown } from '@rocket.chat/fuselage'; -import { useSetting, useUser } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import React, { memo, useRef } from 'react'; -import { createPortal } from 'react-dom'; - -import { UserStatus } from '../../components/UserStatus'; -import UserAvatar from '../../components/avatar/UserAvatar'; -import UserDropdown from './UserDropdown'; -import { useDropdownVisibility } from './hooks/useDropdownVisibility'; - -const anon = { - _id: '', - username: 'Anonymous', - status: 'online', - statusText: '', - avatarETag: undefined, -} as const; - -const UserAvatarButton = (): ReactElement => { - const user = useUser(); - - const { status = !user ? 'online' : 'offline', username, avatarETag, statusText } = user || anon; - const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled'); - - // const allowAnonymousRead = useSetting('Accounts_AllowAnonymousRead'); - - const reference = useRef(null); - const target = useRef(null); - const { isVisible, toggle } = useDropdownVisibility({ reference, target }); - - return ( - <> - <Box - position='relative' - ref={reference} - onClick={(): void => toggle()} - className={css` - cursor: pointer; - `} - data-qa='sidebar-avatar-button' - > - {username && <UserAvatar size='x24' username={username} etag={avatarETag} />} - <Box - className={css` - bottom: 0; - right: 0; - `} - justifyContent='center' - alignItems='center' - display='flex' - overflow='hidden' - size={12} - borderWidth='default' - position='absolute' - bg='surface-tint' - borderColor='extra-light' - borderRadius='full' - mie='neg-x2' - mbe='neg-x2' - > - <UserStatus small status={presenceDisabled ? 'disabled' : status} statusText={statusText} /> - </Box> - </Box> - {user && - isVisible && - createPortal( - <Dropdown reference={reference} ref={target}> - <UserDropdown user={user} onClose={(): void => toggle(false)} /> - </Dropdown>, - document.body, - )} - </> - ); -}; - -export default memo(UserAvatarButton); diff --git a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx new file mode 100644 index 000000000000..f1abaa6a6269 --- /dev/null +++ b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx @@ -0,0 +1,57 @@ +import { css } from '@rocket.chat/css-in-js'; +import { Box } from '@rocket.chat/fuselage'; +import { useSetting, useUser } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { UserStatus } from '../../components/UserStatus'; +import UserAvatar from '../../components/avatar/UserAvatar'; + +const anon = { + _id: '', + username: 'Anonymous', + status: 'online', + statusText: '', + avatarETag: undefined, +} as const; + +const UserAvatarWithStatus = () => { + const user = useUser(); + const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled'); + + const { status = !user ? 'online' : 'offline', username, avatarETag, statusText } = user || anon; + + return ( + <Box + position='relative' + className={css` + cursor: pointer; + `} + aria-label='User menu' + data-qa='sidebar-avatar-button' + > + {username && <UserAvatar size='x24' username={username} etag={avatarETag} />} + <Box + className={css` + bottom: 0; + right: 0; + `} + justifyContent='center' + alignItems='center' + display='flex' + overflow='hidden' + size={12} + borderWidth='default' + position='absolute' + bg='surface-tint' + borderColor='extra-light' + borderRadius='full' + mie='neg-x2' + mbe='neg-x2' + > + <UserStatus small status={presenceDisabled ? 'disabled' : status} statusText={statusText} /> + </Box> + </Box> + ); +}; + +export default UserAvatarWithStatus; diff --git a/apps/meteor/client/sidebar/header/UserDropdown.tsx b/apps/meteor/client/sidebar/header/UserDropdown.tsx deleted file mode 100644 index 8a75891455da..000000000000 --- a/apps/meteor/client/sidebar/header/UserDropdown.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import type { IUser, ValueOf } from '@rocket.chat/core-typings'; -import { UserStatus as UserStatusEnum } from '@rocket.chat/core-typings'; -import { - Box, - Margins, - Option, - OptionColumn, - OptionContent, - OptionDivider, - OptionIcon, - OptionInput, - OptionTitle, - RadioButton, -} from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useLayout, useRoute, useLogout, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; -import { useThemeMode } from '@rocket.chat/ui-theming/src/hooks/useThemeMode'; -import type { ReactElement } from 'react'; -import React from 'react'; - -import { AccountBox } from '../../../app/ui-utils/client'; -import { userStatus } from '../../../app/user-status/client'; -import { callbacks } from '../../../lib/callbacks'; -import MarkdownText from '../../components/MarkdownText'; -import { UserStatus } from '../../components/UserStatus'; -import UserAvatar from '../../components/avatar/UserAvatar'; -import { useUserDisplayName } from '../../hooks/useUserDisplayName'; -import { imperativeModal } from '../../lib/imperativeModal'; -import { useStatusDisabledModal } from '../../views/admin/customUserStatus/hooks/useStatusDisabledModal'; -import EditStatusModal from './EditStatusModal'; - -const isDefaultStatus = (id: string): boolean => (Object.values(UserStatusEnum) as string[]).includes(id); - -const isDefaultStatusName = (_name: string, id: string): _name is UserStatusEnum => isDefaultStatus(id); - -const setStatus = (status: (typeof userStatus.list)['']): void => { - AccountBox.setStatus(status.statusType, !isDefaultStatus(status.id) ? status.name : ''); - void callbacks.run('userStatusManuallySet', status); -}; - -const translateStatusName = (t: ReturnType<typeof useTranslation>, status: (typeof userStatus.list)['']): string => { - if (isDefaultStatusName(status.name, status.id)) { - return t(status.name as TranslationKey); - } - - return status.name; -}; - -type UserDropdownProps = { - user: Pick<IUser, 'username' | 'name' | 'avatarETag' | 'status' | 'statusText'>; - onClose: () => void; -}; - -const UserDropdown = ({ user, onClose }: UserDropdownProps): ReactElement => { - const t = useTranslation(); - const accountRoute = useRoute('account-index'); - const logout = useLogout(); - const { isMobile } = useLayout(); - const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled'); - const handleStatusDisabledModal = useStatusDisabledModal(); - - const [selectedTheme, setTheme] = useThemeMode(); - - const { username, avatarETag, status, statusText } = user; - - const displayName = useUserDisplayName(user); - - const filterInvisibleStatus = !useSetting('Accounts_AllowInvisibleStatusOption') - ? (status: ValueOf<(typeof userStatus)['list']>): boolean => status.name !== 'invisible' - : (): boolean => true; - - const handleCustomStatus = useMutableCallback((e) => { - e.preventDefault(); - imperativeModal.open({ - component: EditStatusModal, - props: { userStatus: status, userStatusText: statusText, onClose: imperativeModal.close }, - }); - onClose(); - }); - - const handleMyAccount = useMutableCallback(() => { - accountRoute.push({}); - onClose(); - }); - - const handleLogout = useMutableCallback(() => { - logout(); - onClose(); - }); - - return ( - <Box display='flex' flexDirection='column' w={!isMobile ? '244px' : undefined}> - <Box pi='x12' display='flex' flexDirection='row' alignItems='center'> - <Box mie='x4'> - <UserAvatar size='x36' username={username || ''} etag={avatarETag} /> - </Box> - <Box mis='x4' display='flex' overflow='hidden' flexDirection='column' fontScale='p2' mb='neg-x4' flexGrow={1} flexShrink={1}> - <Box withTruncatedText w='full' display='flex' alignItems='center' flexDirection='row'> - <Margins inline='x4'> - <UserStatus status={presenceDisabled ? 'disabled' : status} /> - <Box is='span' withTruncatedText display='inline-block' fontWeight='700'> - {displayName} - </Box> - </Margins> - </Box> - <Box color='hint'> - <MarkdownText - withTruncatedText - parseEmoji={true} - content={statusText || t(status ?? 'offline')} - variant='inlineWithoutBreaks' - /> - </Box> - </Box> - </Box> - <OptionDivider /> - <OptionTitle>{t('Status')}</OptionTitle> - {presenceDisabled && ( - <Box fontScale='p2' mi='x12' mb='x4'> - <Box mbe='x4'>{t('User_status_disabled')}</Box> - <Box is='a' color='info' onClick={handleStatusDisabledModal}> - {t('Learn_more')} - </Box> - </Box> - )} - {Object.values(userStatus.list) - .filter(filterInvisibleStatus) - .map((status, i) => { - const name = status.localizeName ? translateStatusName(t, status) : status.name; - const modifier = status.statusType || user.status; - - return ( - <Option - key={i} - disabled={presenceDisabled} - onClick={(): void => { - setStatus(status); - onClose(); - }} - > - <OptionColumn> - <UserStatus status={modifier} /> - </OptionColumn> - <OptionContent> - <MarkdownText content={name} parseEmoji={true} variant='inline' /> - </OptionContent> - </Option> - ); - })} - <Option icon='emoji' label={`${t('Custom_Status')}...`} onClick={handleCustomStatus} disabled={presenceDisabled}></Option> - <OptionDivider /> - - <OptionTitle>{t('Theme')}</OptionTitle> - <Option is='label' role='listitem'> - <OptionIcon name='sun' /> - <OptionContent>{t('Theme_light')}</OptionContent> - <OptionInput> - <RadioButton checked={selectedTheme === 'light'} onChange={setTheme('light')} m='x4' /> - </OptionInput> - </Option> - <Option is='label' role='listitem'> - <OptionIcon name='moon' /> - <OptionContent>{t('Theme_dark')}</OptionContent> - <OptionInput> - <RadioButton checked={selectedTheme === 'dark'} onChange={setTheme('dark')} m='x4' /> - </OptionInput> - </Option> - <Option is='label' role='listitem'> - <OptionIcon name='desktop' /> - <OptionContent>{t('Theme_match_system')}</OptionContent> - <OptionInput> - <RadioButton checked={selectedTheme === 'auto'} onChange={setTheme('auto')} m='x4' /> - </OptionInput> - </Option> - <OptionDivider /> - <Option icon='user' label={t('My_Account')} onClick={handleMyAccount}></Option> - <Option icon='sign-out' label={t('Logout')} onClick={handleLogout}></Option> - </Box> - ); -}; - -export default UserDropdown; diff --git a/apps/meteor/client/sidebar/header/UserMenu.tsx b/apps/meteor/client/sidebar/header/UserMenu.tsx new file mode 100644 index 000000000000..6347f6fad92d --- /dev/null +++ b/apps/meteor/client/sidebar/header/UserMenu.tsx @@ -0,0 +1,31 @@ +import type { IUser } from '@rocket.chat/core-typings'; +import React, { useState, memo } from 'react'; + +import GenericMenu from '../../components/GenericMenu/GenericMenu'; +import type { GenericMenuItemProps } from '../../components/GenericMenu/GenericMenuItem'; +import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; +import UserAvatarWithStatus from './UserAvatarWithStatus'; +import { useUserMenu } from './hooks/useUserMenu'; + +const UserMenu = ({ user }: { user: IUser }) => { + const [isOpen, setIsOpen] = useState(false); + + const sections = useUserMenu(user); + const items = sections.reduce((acc, { items }) => [...acc, ...items], [] as GenericMenuItemProps[]); + + const handleAction = useHandleMenuAction(items, () => setIsOpen(false)); + + return ( + <GenericMenu + icon={<UserAvatarWithStatus />} + selectionMode='multiple' + sections={sections} + title='User menu' + onAction={handleAction} + isOpen={isOpen} + onOpenChange={setIsOpen} + /> + ); +}; + +export default memo(UserMenu); diff --git a/apps/meteor/client/sidebar/header/UserMenuHeader.tsx b/apps/meteor/client/sidebar/header/UserMenuHeader.tsx new file mode 100644 index 000000000000..b56fe35c7ec8 --- /dev/null +++ b/apps/meteor/client/sidebar/header/UserMenuHeader.tsx @@ -0,0 +1,43 @@ +import type { IUser } from '@rocket.chat/core-typings'; +import { Box, Margins } from '@rocket.chat/fuselage'; +import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import MarkdownText from '../../components/MarkdownText'; +import { UserStatus } from '../../components/UserStatus'; +import UserAvatar from '../../components/avatar/UserAvatar'; +import { useUserDisplayName } from '../../hooks/useUserDisplayName'; + +const UserMenuHeader = ({ user }: { user: IUser }) => { + const t = useTranslation(); + const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled'); + const displayName = useUserDisplayName(user); + + return ( + <Box display='flex' flexDirection='row' alignItems='center' minWidth='x208' mbe='neg-x4' mbs='neg-x8'> + <Box mie='x4'> + <UserAvatar size='x36' username={user?.username || ''} etag={user?.avatarETag} /> + </Box> + <Box mis='x4' display='flex' overflow='hidden' flexDirection='column' fontScale='p2' mb='neg-x4' flexGrow={1} flexShrink={1}> + <Box withTruncatedText w='full' display='flex' alignItems='center' flexDirection='row'> + <Margins inline='x4'> + <UserStatus status={presenceDisabled ? 'disabled' : user.status} /> + <Box is='span' withTruncatedText display='inline-block' fontWeight='700'> + {displayName} + </Box> + </Margins> + </Box> + <Box color='hint'> + <MarkdownText + withTruncatedText + parseEmoji={true} + content={user?.statusText || t(user?.status ?? 'offline')} + variant='inlineWithoutBreaks' + /> + </Box> + </Box> + </Box> + ); +}; + +export default UserMenuHeader; diff --git a/apps/meteor/client/sidebar/header/actions/Administration.tsx b/apps/meteor/client/sidebar/header/actions/Administration.tsx index cb6921f91dda..d2e51f191370 100644 --- a/apps/meteor/client/sidebar/header/actions/Administration.tsx +++ b/apps/meteor/client/sidebar/header/actions/Administration.tsx @@ -3,20 +3,15 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes, VFC } from 'react'; import React from 'react'; -import GenericMenu from '../../../components/GenericMenu'; -import type { GenericMenuItemProps } from '../../../components/GenericMenuItem'; -import { useHandleMenuAction } from '../../../hooks/useHandleMenuAction'; +import GenericMenu from '../../../components/GenericMenu/GenericMenu'; import { useAdministrationMenu } from './hooks/useAdministrationMenu'; const Administration: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { const t = useTranslation(); const sections = useAdministrationMenu(); - const items = sections.reduce((acc, { items }) => [...acc, ...items], [] as GenericMenuItemProps[]); - const handleAction = useHandleMenuAction(items); - - return <GenericMenu sections={sections} title={t('Administration')} onAction={handleAction} is={Sidebar.TopBar.Action} {...props} />; + return <GenericMenu sections={sections} title={t('Administration')} is={Sidebar.TopBar.Action} {...props} />; }; export default Administration; diff --git a/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx b/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx index 465bcc63bc05..5289a1a9d8a5 100644 --- a/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx +++ b/apps/meteor/client/sidebar/header/actions/CreateRoom.tsx @@ -3,29 +3,15 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes, VFC } from 'react'; import React from 'react'; -import GenericMenu from '../../../components/GenericMenu'; -import type { GenericMenuItemProps } from '../../../components/GenericMenuItem'; -import { useHandleMenuAction } from '../../../hooks/useHandleMenuAction'; +import GenericMenu from '../../../components/GenericMenu/GenericMenu'; import { useCreateRoom } from './hooks/useCreateRoomMenu'; const CreateRoom: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { const t = useTranslation(); const sections = useCreateRoom(); - const items = sections.reduce((acc, { items }) => [...acc, ...items], [] as GenericMenuItemProps[]); - const handleAction = useHandleMenuAction(items); - - return ( - <GenericMenu - icon='edit-rounded' - sections={sections} - onAction={handleAction} - title={t('Create_new')} - is={Sidebar.TopBar.Action} - {...props} - /> - ); + return <GenericMenu icon='edit-rounded' sections={sections} title={t('Create_new')} is={Sidebar.TopBar.Action} {...props} />; }; export default CreateRoom; diff --git a/apps/meteor/client/sidebar/header/actions/Sort.tsx b/apps/meteor/client/sidebar/header/actions/Sort.tsx index 194e920ec4a0..330ad6b233e6 100644 --- a/apps/meteor/client/sidebar/header/actions/Sort.tsx +++ b/apps/meteor/client/sidebar/header/actions/Sort.tsx @@ -3,7 +3,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import type { VFC, HTMLAttributes } from 'react'; import React from 'react'; -import GenericMenu from '../../../components/GenericMenu'; +import GenericMenu from '../../../components/GenericMenu/GenericMenu'; import { useSortMenu } from './hooks/useSortMenu'; const Sort: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index b32ae66dde13..3c8dada44e51 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -7,7 +7,7 @@ import type { AccountBoxItem } from '../../../../../app/ui-utils/client/lib/Acco import type { UpgradeTabVariant } from '../../../../../lib/upgradeTab'; import { getUpgradeTabLabel, isFullyFeature } from '../../../../../lib/upgradeTab'; import Emoji from '../../../../components/Emoji'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; import RegisterWorkspaceModal from '../../../../views/admin/cloud/modals/RegisterWorkspaceModal'; import { useUpgradeTabParams } from '../../../../views/hooks/useUpgradeTabParams'; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx index 66a5befe0f2b..a6c77c55d284 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { triggerActionButtonAction } from '../../../../../app/ui-message/client/ActionManager'; import type { IAppAccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; import { useAppRequestStats } from '../../../../views/marketplace/hooks/useAppRequestStats'; type useAppsItemsProps = { diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx index 3e9d98d74b18..3ed3ba94dcb9 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx @@ -1,6 +1,6 @@ import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; type useAuditItemsProps = { showAudit: boolean; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx index 3cdca7adebc4..65dd9e29726b 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomItems.tsx @@ -1,7 +1,7 @@ import { useTranslation, useSetting, useAtLeastOnePermission } from '@rocket.chat/ui-contexts'; import CreateDiscussion from '../../../../components/CreateDiscussion'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; import { useIsEnterprise } from '../../../../hooks/useIsEnterprise'; import CreateChannelWithData from '../../CreateChannel'; import CreateDirectMessage from '../../CreateDirectMessage'; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx index 24629be905e3..07223513fae9 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.tsx @@ -2,7 +2,7 @@ import { CheckBox } from '@rocket.chat/fuselage'; import { useEndpoint, useUserPreference, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; export const useGroupingListItems = (): GenericMenuItemProps[] => { const t = useTranslation(); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx index 76cd6d9458af..b3ac63f13773 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useMatrixFederationItems.tsx.tsx @@ -1,6 +1,6 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; import MatrixFederationSearch from '../../MatrixFederationSearch'; import { useCreateRoomModal } from '../../hooks/useCreateRoomModal'; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx index c200fefd5689..8adc0580cff8 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.tsx @@ -2,45 +2,39 @@ import { RadioButton } from '@rocket.chat/fuselage'; import { useEndpoint, useUserPreference, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; -import { OmnichannelSortingDisclaimer } from '../../../../components/Omnichannel/OmnichannelSortingDisclaimer'; -import { useOmnichannelEnterpriseEnabled } from '../../../../hooks/omnichannel/useOmnichannelEnterpriseEnabled'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; +import { + OmnichannelSortingDisclaimer, + useOmnichannelSortingDisclaimer, +} from '../../../../components/Omnichannel/OmnichannelSortingDisclaimer'; export const useSortModeItems = (): GenericMenuItemProps[] => { const t = useTranslation(); const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); const sidebarSortBy = useUserPreference<'activity' | 'alphabetical'>('sidebarSortby', 'activity'); - const isOmnichannelEnabled = useOmnichannelEnterpriseEnabled(); - - const omniDisclaimerItem = { - id: 'sortByList', - content: <OmnichannelSortingDisclaimer id='sortByList' />, - }; + const isOmnichannelEnabled = useOmnichannelSortingDisclaimer(); const useHandleChange = (value: 'alphabetical' | 'activity'): (() => void) => useCallback(() => saveUserPreferences({ data: { sidebarSortby: value } }), [value]); const setToAlphabetical = useHandleChange('alphabetical'); const setToActivity = useHandleChange('activity'); - const items: GenericMenuItemProps[] = [ + + return [ { id: 'activity', content: t('Activity'), icon: 'clock', addon: <RadioButton mi='x16' onChange={setToActivity} checked={sidebarSortBy === 'activity'} />, + description: sidebarSortBy === 'activity' && isOmnichannelEnabled && <OmnichannelSortingDisclaimer />, }, { id: 'name', content: t('Name'), icon: 'sort-az', addon: <RadioButton mi='x16' onChange={setToAlphabetical} checked={sidebarSortBy === 'alphabetical'} />, + description: sidebarSortBy === 'alphabetical' && isOmnichannelEnabled && <OmnichannelSortingDisclaimer />, }, ]; - - if (isOmnichannelEnabled) { - items.push(omniDisclaimerItem); - } - - return items; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx index 92da7101baf5..17f052fefca1 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.tsx @@ -2,7 +2,7 @@ import { RadioButton, ToggleSwitch } from '@rocket.chat/fuselage'; import { useEndpoint, useUserPreference, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; -import type { GenericMenuItemProps } from '../../../../components/GenericMenuItem'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; export const useViewModeItems = (): GenericMenuItemProps[] => { const t = useTranslation(); diff --git a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx new file mode 100644 index 000000000000..b46923a562e8 --- /dev/null +++ b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx @@ -0,0 +1,33 @@ +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useLogout, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; + +import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; + +export const useAccountItems = (): GenericMenuItemProps[] => { + const t = useTranslation(); + const accountRoute = useRoute('account-index'); + const logout = useLogout(); + + const handleMyAccount = useMutableCallback(() => { + accountRoute.push({}); + }); + + const handleLogout = useMutableCallback(() => { + logout(); + }); + + return [ + { + id: 'my-account', + icon: 'user', + content: t('My_Account'), + onClick: handleMyAccount, + }, + { + id: 'logout', + icon: 'sign-out', + content: t('Logout'), + onClick: handleLogout, + }, + ]; +}; diff --git a/apps/meteor/client/sidebar/header/hooks/useCustomStatusModalHandler.tsx b/apps/meteor/client/sidebar/header/hooks/useCustomStatusModalHandler.tsx new file mode 100644 index 000000000000..f0f863f8efab --- /dev/null +++ b/apps/meteor/client/sidebar/header/hooks/useCustomStatusModalHandler.tsx @@ -0,0 +1,14 @@ +import { useSetModal, useUser } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import EditStatusModal from '../EditStatusModal'; + +export const useCustomStatusModalHandler = () => { + const user = useUser(); + const setModal = useSetModal(); + + return () => { + const handleModalClose = () => setModal(null); + setModal(<EditStatusModal userStatus={user?.status} userStatusText={user?.statusText} onClose={handleModalClose} />); + }; +}; diff --git a/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx new file mode 100644 index 000000000000..8621846456f1 --- /dev/null +++ b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx @@ -0,0 +1,77 @@ +import type { IUser, ValueOf } from '@rocket.chat/core-typings'; +import { UserStatus as UserStatusEnum } from '@rocket.chat/core-typings'; +import { Box } from '@rocket.chat/fuselage'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { AccountBox } from '../../../../app/ui-utils/client'; +import { userStatus } from '../../../../app/user-status/client'; +import { callbacks } from '../../../../lib/callbacks'; +import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; +import MarkdownText from '../../../components/MarkdownText'; +import { UserStatus } from '../../../components/UserStatus'; +import { useStatusDisabledModal } from '../../../views/admin/customUserStatus/hooks/useStatusDisabledModal'; +import { useCustomStatusModalHandler } from './useCustomStatusModalHandler'; + +const isDefaultStatus = (id: string): boolean => (Object.values(UserStatusEnum) as string[]).includes(id); +const isDefaultStatusName = (_name: string, id: string): _name is UserStatusEnum => isDefaultStatus(id); +const translateStatusName = (t: ReturnType<typeof useTranslation>, status: (typeof userStatus.list)['']): string => { + if (isDefaultStatusName(status.name, status.id)) { + return t(status.name as TranslationKey); + } + + return status.name; +}; + +export const useStatusItems = (user: IUser): GenericMenuItemProps[] => { + const t = useTranslation(); + const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled'); + + const setStatus = (status: (typeof userStatus.list)['']): void => { + AccountBox.setStatus(status.statusType, !isDefaultStatus(status.id) ? status.name : ''); + void callbacks.run('userStatusManuallySet', status); + }; + + const filterInvisibleStatus = !useSetting('Accounts_AllowInvisibleStatusOption') + ? (status: ValueOf<(typeof userStatus)['list']>): boolean => status.name !== 'invisible' + : (): boolean => true; + + const handleCustomStatus = useCustomStatusModalHandler(); + + const handleStatusDisabledModal = useStatusDisabledModal(); + + const presenceDisabledItem = { + id: 'presence-disabled', + content: ( + <Box fontScale='p2'> + <Box mbe='x4' wordBreak='break-word' style={{ whiteSpace: 'normal' }}> + {t('User_status_disabled')} + </Box> + <Box is='a' color='info' onClick={handleStatusDisabledModal}> + {t('Learn_more')} + </Box> + </Box> + ), + }; + + const statusItems = Object.values(userStatus.list) + .filter(filterInvisibleStatus) + .map((status) => { + const name = status.localizeName ? translateStatusName(t, status) : status.name; + const modifier = status.statusType || user?.status; + return { + id: status.id, + status: <UserStatus status={modifier} />, + content: <MarkdownText content={name} parseEmoji={true} variant='inline' />, + onClick: () => setStatus(status), + disabled: presenceDisabled, + }; + }); + + return [ + ...(presenceDisabled ? [presenceDisabledItem] : []), + ...statusItems, + { id: 'custom-status', icon: 'emoji', content: t('Custom_Status'), onClick: handleCustomStatus, disabled: presenceDisabled }, + ]; +}; diff --git a/apps/meteor/client/sidebar/header/hooks/useThemeItems.tsx b/apps/meteor/client/sidebar/header/hooks/useThemeItems.tsx new file mode 100644 index 000000000000..d571acde1624 --- /dev/null +++ b/apps/meteor/client/sidebar/header/hooks/useThemeItems.tsx @@ -0,0 +1,33 @@ +import { RadioButton } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useThemeMode } from '@rocket.chat/ui-theming/src/hooks/useThemeMode'; +import React from 'react'; + +import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; + +export const useThemeItems = (): GenericMenuItemProps[] => { + const t = useTranslation(); + + const [selectedTheme, setTheme] = useThemeMode(); + + return [ + { + id: 'light', + icon: 'sun', + content: t('Theme_light'), + addon: <RadioButton checked={selectedTheme === 'light'} onChange={setTheme('light')} m='x4' />, + }, + { + id: 'dark', + icon: 'moon', + content: t('Theme_dark'), + addon: <RadioButton checked={selectedTheme === 'dark'} onChange={setTheme('dark')} m='x4' />, + }, + { + id: 'auto', + icon: 'desktop', + content: t('Theme_match_system'), + addon: <RadioButton checked={selectedTheme === 'auto'} onChange={setTheme('auto')} m='x4' />, + }, + ]; +}; diff --git a/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx b/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx new file mode 100644 index 000000000000..4458deb26f42 --- /dev/null +++ b/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx @@ -0,0 +1,31 @@ +import type { IUser } from '@rocket.chat/core-typings'; +import React from 'react'; + +import UserMenuHeader from '../UserMenuHeader'; +import { useAccountItems } from './useAccountItems'; +import { useStatusItems } from './useStatusItems'; +import { useThemeItems } from './useThemeItems'; + +export const useUserMenu = (user: IUser) => { + const statusItems = useStatusItems(user); + const themeItems = useThemeItems(); + const accountItems = useAccountItems(); + + return [ + { + title: <UserMenuHeader user={user} />, + items: [], + }, + { + title: 'Status', + items: statusItems, + }, + { + title: 'Theme', + items: themeItems, + }, + { + items: accountItems, + }, + ]; +}; diff --git a/apps/meteor/client/sidebar/header/index.tsx b/apps/meteor/client/sidebar/header/index.tsx index 79790341a24e..1027474aaec2 100644 --- a/apps/meteor/client/sidebar/header/index.tsx +++ b/apps/meteor/client/sidebar/header/index.tsx @@ -3,7 +3,8 @@ import { useUser, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; -import UserAvatarButton from './UserAvatarButton'; +import UserAvatarWithStatus from './UserAvatarWithStatus'; +import UserMenu from './UserMenu'; import Administration from './actions/Administration'; import CreateRoom from './actions/CreateRoom'; import Directory from './actions/Directory'; @@ -24,7 +25,7 @@ const HeaderWithData = (): ReactElement => { style: { flexShrink: 0 }, }} > - <UserAvatarButton /> + {user ? <UserMenu user={user} /> : <UserAvatarWithStatus />} <Sidebar.TopBar.Actions> <Home title={t('Home')} /> <Search title={t('Search')} /> diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts index e3ed090689c5..e413caaa5436 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-auto-onhold-chat-closing.spec.ts @@ -38,7 +38,7 @@ test.describe('omnichannel-auto-onhold-chat-closing', () => { test.beforeEach(async ({ page, api }) => { // make "user-1" online - await agent.poHomeChannel.sidenav.switchStatus('online'); + await agent.poHomeChannel.sidenav.switchStatus('Online online'); // start a new chat for each test newVisitor = { diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts index d4cc6484975e..dc9f745c2740 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-changing-room-priority-and-sla.spec.ts @@ -41,7 +41,7 @@ test.describe('omnichannel-changing-room-priority-and-sla', () => { const { page } = await createAuxContext(browser, Users.admin); agent = { page, poHomeChannel: new HomeChannel(page) }; - await agent.poHomeChannel.sidenav.switchStatus('online'); + await agent.poHomeChannel.sidenav.switchStatus('Online online'); }); test.afterAll(async ({ api }) => { diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts index 4738158ca23c..78e0a1362da9 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-takeChat.spec.ts @@ -35,7 +35,7 @@ test.describe('omnichannel-takeChat', () => { test.beforeEach(async ({ page, api }) => { // make "user-1" online - await agent.poHomeChannel.sidenav.switchStatus('online'); + await agent.poHomeChannel.sidenav.switchStatus('Online online'); // start a new chat for each test newVisitor = { @@ -62,7 +62,7 @@ test.describe('omnichannel-takeChat', () => { test('expect "user1" to not able able to take chat from queue in-case its user status is offline', async () => { // make "user-1" offline - await agent.poHomeChannel.sidenav.switchStatus('offline'); + await agent.poHomeChannel.sidenav.switchStatus('Offline offline'); await agent.poHomeChannel.sidenav.openQueuedOmnichannelChat(newVisitor.name); await expect(agent.poHomeChannel.content.takeOmnichannelChatButton).toBeVisible(); diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts index 3c74065a9a84..6e3e274bb436 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts @@ -40,8 +40,8 @@ test.describe('omnichannel-transfer-to-another-agent', () => { }); test.beforeEach(async ({ page, api }) => { // make "user-1" online & "user-2" offline so that chat can be automatically routed to "user-1" - await agent1.poHomeOmnichannel.sidenav.switchStatus('online'); - await agent2.poHomeOmnichannel.sidenav.switchStatus('offline'); + await agent1.poHomeOmnichannel.sidenav.switchStatus('Online online'); + await agent2.poHomeOmnichannel.sidenav.switchStatus('Offline offline'); // start a new chat for each test newVisitor = { @@ -62,7 +62,7 @@ test.describe('omnichannel-transfer-to-another-agent', () => { }); await test.step('Expect to not be able to transfer chat to "user-2" when that user is offline', async () => { - await agent2.poHomeOmnichannel.sidenav.switchStatus('offline'); + await agent2.poHomeOmnichannel.sidenav.switchStatus('Offline offline'); await agent1.poHomeOmnichannel.content.btnForwardChat.click(); await agent1.poHomeOmnichannel.content.inputModalAgentUserName.type('user2'); @@ -72,7 +72,7 @@ test.describe('omnichannel-transfer-to-another-agent', () => { }); await test.step('Expect to be able to transfer an omnichannel to conversation to agent 2 as agent 1 when agent 2 is online', async () => { - await agent2.poHomeOmnichannel.sidenav.switchStatus('online'); + await agent2.poHomeOmnichannel.sidenav.switchStatus('Online online'); await agent1.poHomeOmnichannel.sidenav.getSidebarItemByName(newVisitor.name).click(); await agent1.poHomeOmnichannel.content.btnForwardChat.click(); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts index 8b721895cc5c..3d2ddd3921d6 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-sidenav.ts @@ -59,9 +59,9 @@ export class HomeSidenav { await this.page.locator('//*[contains(@class, "rcx-option__content") and contains(text(), "Logout")]').click(); } - async switchStatus(status: 'offline' | 'online'): Promise<void> { + async switchStatus(status: 'Offline offline' | 'Online online'): Promise<void> { await this.page.locator('[data-qa="sidebar-avatar-button"]').click(); - await this.page.locator(`//li[@class="rcx-option"]//div[contains(text(), "${status}")]`).click(); + await this.page.locator(`role=menuitemcheckbox[name="${status}"]`).click(); } async openChat(name: string): Promise<void> { From 93fff202ee031394d7652a00a8d25b651c37db9c Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Tue, 4 Jul 2023 20:59:53 -0300 Subject: [PATCH 058/149] fix(meteor): `room-opened` event not dispatching when navigating cached rooms (#29718) --- .changeset/hip-hornets-fail.md | 5 +++++ apps/meteor/client/views/room/hooks/useOpenRoom.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .changeset/hip-hornets-fail.md diff --git a/.changeset/hip-hornets-fail.md b/.changeset/hip-hornets-fail.md new file mode 100644 index 000000000000..5bd0c0d15199 --- /dev/null +++ b/.changeset/hip-hornets-fail.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed `room-opened` event not dispatching when navigating cached rooms diff --git a/apps/meteor/client/views/room/hooks/useOpenRoom.ts b/apps/meteor/client/views/room/hooks/useOpenRoom.ts index 8b9f5fcdf75a..288767ccde53 100644 --- a/apps/meteor/client/views/room/hooks/useOpenRoom.ts +++ b/apps/meteor/client/views/room/hooks/useOpenRoom.ts @@ -1,6 +1,7 @@ import type { IRoom, RoomType } from '@rocket.chat/core-typings'; import { useMethod, useRoute, useSetting, useUser } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; +import { useRef } from 'react'; import { ChatRoom, ChatSubscription } from '../../../../app/models/client'; import { LegacyRoomManager } from '../../../../app/ui-utils/client'; @@ -20,6 +21,8 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st const openRoom = useMethod('openRoom'); const directRoute = useRoute('direct'); + const unsubscribeFromRoomOpenedEvent = useRef<() => void>(() => undefined); + return useQuery( // we need to add uid and username here because `user` is not loaded all at once (see UserProvider -> Meteor.user()) ['rooms', { type, reference }, { uid: user?._id, username: user?.username }] as const, @@ -63,14 +66,15 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st throw new RoomNotFoundError(undefined, { rid: room._id }); } + unsubscribeFromRoomOpenedEvent.current(); + unsubscribeFromRoomOpenedEvent.current = RoomManager.once('opened', () => fireGlobalEvent('room-opened', omit(room, 'usernames'))); + LegacyRoomManager.open({ typeName: type + reference, rid: room._id }); if (room._id === RoomManager.opened) { return { rid: room._id }; } - fireGlobalEvent('room-opened', omit(room, 'usernames')); - // update user's room subscription const sub = ChatSubscription.findOne({ rid: room._id }); if (sub && !sub.open) { From bec9dfdf88a80716cb7b32a6d484d4294ecf984c Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Wed, 5 Jul 2023 11:00:32 -0300 Subject: [PATCH 059/149] regression: VoIP call UI becoming non responsive (#29717) --- .../providers/CallProvider/CallProvider.tsx | 51 ++++--------------- .../CallProvider/hooks/useVoipSounds.ts | 29 +++++++++++ 2 files changed, 39 insertions(+), 41 deletions(-) create mode 100644 apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts diff --git a/apps/meteor/client/providers/CallProvider/CallProvider.tsx b/apps/meteor/client/providers/CallProvider/CallProvider.tsx index 5464e9707e46..22ef506ad481 100644 --- a/apps/meteor/client/providers/CallProvider/CallProvider.tsx +++ b/apps/meteor/client/providers/CallProvider/CallProvider.tsx @@ -1,12 +1,4 @@ -import type { - IVoipRoom, - IUser, - VoipEventDataSignature, - ICallerInfo, - ICallDetails, - ILivechatVisitor, - Serialized, -} from '@rocket.chat/core-typings'; +import type { IVoipRoom, VoipEventDataSignature, ICallerInfo, ICallDetails, ILivechatVisitor, Serialized } from '@rocket.chat/core-typings'; import { VoipClientEvents, isVoipEventAgentCalled, @@ -37,8 +29,6 @@ import React, { useMemo, useRef, useCallback, useEffect, useState } from 'react' import { createPortal } from 'react-dom'; import type { OutgoingByeRequest } from 'sip.js/lib/core'; -import { CustomSounds } from '../../../app/custom-sounds/client'; -import { getUserPreference } from '../../../app/utils/client'; import { isOutboundClient, useVoipClient } from '../../../ee/client/hooks/useVoipClient'; import { parseOutboundPhoneNumber } from '../../../ee/client/lib/voip/parseOutboundPhoneNumber'; import { WrapUpCallModal } from '../../../ee/client/voip/components/modals/WrapUpCallModal'; @@ -47,30 +37,7 @@ import { CallContext, useIsVoipEnterprise } from '../../contexts/CallContext'; import { useDialModal } from '../../hooks/useDialModal'; import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; import type { QueueAggregator } from '../../lib/voip/QueueAggregator'; - -type VoipSound = 'telephone' | 'outbound-call-ringing' | 'call-ended'; - -const startRingback = (user: IUser, soundId: VoipSound, loop = true): void => { - const audioVolume = getUserPreference(user, 'notificationsSoundVolume', 100) as number; - CustomSounds.play(soundId, { - volume: Number((audioVolume / 100).toPrecision(2)), - loop, - }); -}; - -const stopRingBackById = (soundId: VoipSound): void => { - const sound = CustomSounds.getSound(soundId); - CustomSounds.pause(soundId); - CustomSounds.remove(sound); -}; - -const stopTelephoneRingback = (): void => stopRingBackById('telephone'); -const stopOutboundCallRinging = (): void => stopRingBackById('outbound-call-ringing'); - -const stopAllRingback = (): void => { - stopTelephoneRingback(); - stopOutboundCallRinging(); -}; +import { useVoipSounds } from './hooks/useVoipSounds'; type NetworkState = 'online' | 'offline'; @@ -103,6 +70,8 @@ export const CallProvider: FC = ({ children }) => { const { openDialModal } = useDialModal(); + const voipSounds = useVoipSounds(); + const closeRoom = useCallback( async (data = {}): Promise<void> => { roomInfo && @@ -359,7 +328,7 @@ export const CallProvider: FC = ({ children }) => { if (!callDetails.callInfo) { return; } - stopAllRingback(); + voipSounds.stopAll(); if (callDetails.userState !== UserState.UAC) { return; } @@ -400,16 +369,16 @@ export const CallProvider: FC = ({ children }) => { }; const onRinging = (): void => { - startRingback(user, 'outbound-call-ringing'); + voipSounds.play('outbound-call-ringing'); }; const onIncomingCallRinging = (): void => { - startRingback(user, 'telephone'); + voipSounds.play('telephone'); }; const onCallTerminated = (): void => { - startRingback(user, 'call-ended', false); - stopAllRingback(); + voipSounds.play('call-ended', false); + voipSounds.stopAll(); }; const onCallFailed = (reason: 'Not Found' | 'Address Incomplete' | 'Request Terminated' | string): void => { @@ -458,7 +427,7 @@ export const CallProvider: FC = ({ children }) => { result.voipClient?.off('callfailed', onCallFailed); } }; - }, [createRoom, dispatchEvent, networkStatus, openDialModal, result.voipClient, t, user]); + }, [createRoom, dispatchEvent, networkStatus, openDialModal, result.voipClient, voipSounds, t, user]); const contextValue: CallContextValue = useMemo(() => { if (!voipEnabled) { diff --git a/apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts b/apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts new file mode 100644 index 000000000000..44e9f19e72e6 --- /dev/null +++ b/apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts @@ -0,0 +1,29 @@ +import { useCustomSound, useUser } from '@rocket.chat/ui-contexts'; +import { useMemo } from 'react'; + +import { getUserPreference } from '../../../../app/utils/client'; + +type VoipSound = 'telephone' | 'outbound-call-ringing' | 'call-ended'; + +export const useVoipSounds = () => { + const { play, pause } = useCustomSound(); + const user = useUser(); + + return useMemo( + () => ({ + play: (soundId: VoipSound, loop = true) => { + const audioVolume = getUserPreference(user, 'notificationsSoundVolume', 100) as number; + play(soundId, { + volume: Number((audioVolume / 100).toPrecision(2)), + loop, + }); + }, + stop: (soundId: VoipSound) => pause(soundId), + stopAll: () => { + pause('telephone'); + pause('outbound-call-ringing'); + }, + }), + [play, pause, user], + ); +}; From e2d6aefc41d04fc477c60562f281b6adf743f865 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Wed, 5 Jul 2023 11:46:20 -0300 Subject: [PATCH 060/149] regression: Livechat not unsubscribing after the chat is closed (#29722) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../app/livechat/server/roomAccessValidator.compatibility.ts | 2 +- packages/ddp-client/src/livechat/LivechatClientImpl.ts | 5 +++++ packages/livechat/src/lib/room.js | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts index 0bad086e738b..5da49ccd1994 100644 --- a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts +++ b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts @@ -35,7 +35,7 @@ export const validators: OmnichannelRoomAccessValidator[] = [ } } - return extraData?.visitorToken && room.v && room.v.token === extraData.visitorToken; + return extraData?.visitorToken && room.v && room.v.token === extraData.visitorToken && room.open === true; }, async function (room, user) { if (!user?._id) { diff --git a/packages/ddp-client/src/livechat/LivechatClientImpl.ts b/packages/ddp-client/src/livechat/LivechatClientImpl.ts index 9274c25c5c26..8005528b35c8 100644 --- a/packages/ddp-client/src/livechat/LivechatClientImpl.ts +++ b/packages/ddp-client/src/livechat/LivechatClientImpl.ts @@ -359,6 +359,11 @@ export class LivechatClientImpl extends DDPSDK implements LivechatStream, Livech return this.rest.put(`/v1/livechat/message/${id}`, params); } + unsubscribeAll(): Promise<unknown> { + const subscriptions = Array.from(this.client.subscriptions.keys()); + return Promise.all(subscriptions.map((subscription) => this.client.unsubscribe(subscription))); + } + static create(url: string, retryOptions = { retryCount: 3, retryTime: 10000 }): LivechatClientImpl { // TODO: Decide what to do with the EJSON objects const ddp = new DDPDispatcher(); diff --git a/packages/livechat/src/lib/room.js b/packages/livechat/src/lib/room.js index d80cbd4da88f..adf727248ad6 100644 --- a/packages/livechat/src/lib/room.js +++ b/packages/livechat/src/lib/room.js @@ -17,6 +17,8 @@ import { handleTranscript } from './transcript'; const commands = new Commands(); export const closeChat = async ({ transcriptRequested } = {}) => { + Livechat.unsubscribeAll(); + if (!transcriptRequested) { await handleTranscript(); } From dbdf45b0e59c81582274b640c286c8240aa2beda Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto <tiago.evangelista@rocket.chat> Date: Wed, 5 Jul 2023 12:01:53 -0300 Subject: [PATCH 061/149] feat: Introduce contextualBar surface renderer for UiKit blocks (#29697) --- .changeset/popular-donkeys-laugh.md | 7 ++ .../views/room/contextualBar/Apps/Apps.tsx | 10 +-- .../src/contexts/SurfaceContext.ts | 7 +- .../src/contexts/kitContext.ts | 2 +- .../src/surfaces/ContextualBarSurface.tsx | 18 +++++ .../FuselageContextualBarRenderer.tsx | 15 +++++ .../fuselage-ui-kit/src/surfaces/index.ts | 7 ++ .../src/utils/UiKitComponent.tsx | 13 +++- packages/uikit-playground/package.json | 1 + .../Display/Surface/ContextualBarSurface.tsx | 67 +++++++++++++++++++ .../Preview/Display/Surface/Surface.tsx | 2 + .../src/Components/SurfaceSelect/options.ts | 1 + yarn.lock | 14 ++++ 13 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 .changeset/popular-donkeys-laugh.md create mode 100644 packages/fuselage-ui-kit/src/surfaces/ContextualBarSurface.tsx create mode 100644 packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx create mode 100644 packages/uikit-playground/src/Components/Preview/Display/Surface/ContextualBarSurface.tsx diff --git a/.changeset/popular-donkeys-laugh.md b/.changeset/popular-donkeys-laugh.md new file mode 100644 index 000000000000..4fed70427f7a --- /dev/null +++ b/.changeset/popular-donkeys-laugh.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/fuselage-ui-kit": minor +"@rocket.chat/uikit-playground": minor +--- + +feat: Introduce contextualBar surface renderer for UiKit blocks diff --git a/apps/meteor/client/views/room/contextualBar/Apps/Apps.tsx b/apps/meteor/client/views/room/contextualBar/Apps/Apps.tsx index 1c353ff9a323..0c83a4097016 100644 --- a/apps/meteor/client/views/room/contextualBar/Apps/Apps.tsx +++ b/apps/meteor/client/views/room/contextualBar/Apps/Apps.tsx @@ -1,6 +1,6 @@ import type { IUIKitSurface } from '@rocket.chat/apps-engine/definition/uikit'; import { ButtonGroup, Button, Box, Avatar } from '@rocket.chat/fuselage'; -import { UiKitComponent, UiKitModal, modalParser } from '@rocket.chat/fuselage-ui-kit'; +import { UiKitComponent, UiKitContextualBar, contextualBarParser } from '@rocket.chat/fuselage-ui-kit'; import type { LayoutBlock } from '@rocket.chat/ui-kit'; import { BlockContext } from '@rocket.chat/ui-kit'; import React from 'react'; @@ -27,24 +27,24 @@ const Apps = ({ view, onSubmit, onClose, onCancel, appId }: AppsProps): JSX.Elem <> <ContextualbarHeader> <Avatar url={getURL(`/api/apps/${appId}/icon`)} /> - <ContextualbarTitle>{modalParser.text(view.title, BlockContext.NONE, 0)}</ContextualbarTitle> + <ContextualbarTitle>{contextualBarParser.text(view.title, BlockContext.NONE, 0)}</ContextualbarTitle> {onClose && <ContextualbarClose onClick={onClose} />} </ContextualbarHeader> <ContextualbarScrollableContent> <Box is='form' method='post' action='#' onSubmit={onSubmit}> - <UiKitComponent render={UiKitModal} blocks={view.blocks as LayoutBlock[]} /> + <UiKitComponent render={UiKitContextualBar} blocks={view.blocks as LayoutBlock[]} /> </Box> </ContextualbarScrollableContent> <ContextualbarFooter> <ButtonGroup align='end'> {view.close && ( <Button danger={view.close.style === 'danger'} onClick={onCancel}> - {modalParser.text(view.close.text, BlockContext.NONE, 0)} + {contextualBarParser.text(view.close.text, BlockContext.NONE, 0)} </Button> )} {view.submit && ( <Button {...getButtonStyle(view)} onClick={onSubmit}> - {modalParser.text(view.submit.text, BlockContext.NONE, 1)} + {contextualBarParser.text(view.submit.text, BlockContext.NONE, 1)} </Button> )} </ButtonGroup> diff --git a/packages/fuselage-ui-kit/src/contexts/SurfaceContext.ts b/packages/fuselage-ui-kit/src/contexts/SurfaceContext.ts index 62bd57bd4bf9..21d56b91e457 100644 --- a/packages/fuselage-ui-kit/src/contexts/SurfaceContext.ts +++ b/packages/fuselage-ui-kit/src/contexts/SurfaceContext.ts @@ -1,6 +1,11 @@ import { createContext, useContext } from 'react'; -type SurfaceContextValue = 'attachment' | 'banner' | 'message' | 'modal'; +type SurfaceContextValue = + | 'attachment' + | 'banner' + | 'message' + | 'modal' + | 'contextualBar'; export const SurfaceContext = createContext<SurfaceContextValue>('message'); diff --git a/packages/fuselage-ui-kit/src/contexts/kitContext.ts b/packages/fuselage-ui-kit/src/contexts/kitContext.ts index 6eebb8cea7a6..f4a0687e5af0 100644 --- a/packages/fuselage-ui-kit/src/contexts/kitContext.ts +++ b/packages/fuselage-ui-kit/src/contexts/kitContext.ts @@ -49,6 +49,6 @@ export const useUiKitStateValue = <T extends string | number | undefined>( return { value: (values && (values[actionId]?.value as T)) ?? initialValue, - error: errors && errors[actionId], + error: errors?.[actionId], }; }; diff --git a/packages/fuselage-ui-kit/src/surfaces/ContextualBarSurface.tsx b/packages/fuselage-ui-kit/src/surfaces/ContextualBarSurface.tsx new file mode 100644 index 000000000000..b419dea19e8a --- /dev/null +++ b/packages/fuselage-ui-kit/src/surfaces/ContextualBarSurface.tsx @@ -0,0 +1,18 @@ +import { Margins } from '@rocket.chat/fuselage'; +import type { ReactElement, ReactNode } from 'react'; + +import { Surface } from './Surface'; + +type ContextualBarSurfaceProps = { + children?: ReactNode; +}; + +const ContextualBarSurface = ({ + children, +}: ContextualBarSurfaceProps): ReactElement => ( + <Surface type='contextualBar'> + <Margins blockEnd='x16'>{children}</Margins> + </Surface> +); + +export default ContextualBarSurface; diff --git a/packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx b/packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx new file mode 100644 index 000000000000..ebe6453ba360 --- /dev/null +++ b/packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx @@ -0,0 +1,15 @@ +import { FuselageSurfaceRenderer } from './FuselageSurfaceRenderer'; + +export class FuselageContextualBarSurfaceRenderer extends FuselageSurfaceRenderer { + public constructor() { + super([ + 'actions', + 'context', + 'divider', + 'image', + 'input', + 'section', + 'preview', + ]); + } +} diff --git a/packages/fuselage-ui-kit/src/surfaces/index.ts b/packages/fuselage-ui-kit/src/surfaces/index.ts index 25114be862d0..489fe921ddb5 100644 --- a/packages/fuselage-ui-kit/src/surfaces/index.ts +++ b/packages/fuselage-ui-kit/src/surfaces/index.ts @@ -5,11 +5,14 @@ import ModalSurface from './ModalSurface'; import { createSurfaceRenderer } from './createSurfaceRenderer'; import { FuselageMessageSurfaceRenderer } from './MessageSurfaceRenderer'; import { FuselageModalSurfaceRenderer } from './FuselageModalSurfaceRenderer'; +import { FuselageContextualBarSurfaceRenderer } from './FuselageContextualBarRenderer'; +import ContextualBarSurface from './ContextualBarSurface'; // export const attachmentParser = new FuselageSurfaceRenderer(); export const bannerParser = new FuselageSurfaceRenderer(); export const messageParser = new FuselageMessageSurfaceRenderer(); export const modalParser = new FuselageModalSurfaceRenderer(); +export const contextualBarParser = new FuselageContextualBarSurfaceRenderer(); // export const UiKitAttachment = createSurfaceRenderer(AttachmentSurface, attachmentParser); export const UiKitBanner = createSurfaceRenderer(BannerSurface, bannerParser); @@ -18,3 +21,7 @@ export const UiKitMessage = createSurfaceRenderer( messageParser ); export const UiKitModal = createSurfaceRenderer(ModalSurface, modalParser); +export const UiKitContextualBar = createSurfaceRenderer( + ContextualBarSurface, + contextualBarParser +); diff --git a/packages/fuselage-ui-kit/src/utils/UiKitComponent.tsx b/packages/fuselage-ui-kit/src/utils/UiKitComponent.tsx index f43057a6abc5..3d1c5a12afee 100644 --- a/packages/fuselage-ui-kit/src/utils/UiKitComponent.tsx +++ b/packages/fuselage-ui-kit/src/utils/UiKitComponent.tsx @@ -1,10 +1,19 @@ import type * as UiKit from '@rocket.chat/ui-kit'; import type { ReactElement } from 'react'; -import type { UiKitBanner, UiKitMessage, UiKitModal } from '../surfaces'; +import type { + UiKitBanner, + UiKitContextualBar, + UiKitMessage, + UiKitModal, +} from '../surfaces'; type UiKitComponentProps = { - render: typeof UiKitBanner | typeof UiKitMessage | typeof UiKitModal; + render: + | typeof UiKitBanner + | typeof UiKitMessage + | typeof UiKitModal + | typeof UiKitContextualBar; blocks: UiKit.LayoutBlock[]; }; diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index a309352c04f4..c60b02b9ca00 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -26,6 +26,7 @@ "@rocket.chat/ui-contexts": "workspace:~", "codemirror": "^6.0.1", "eslint4b-prebuilt": "^6.7.2", + "rc-scrollbars": "^1.1.6", "react": "^17.0.2", "react-beautiful-dnd": "^13.1.1", "react-dom": "^17.0.2", diff --git a/packages/uikit-playground/src/Components/Preview/Display/Surface/ContextualBarSurface.tsx b/packages/uikit-playground/src/Components/Preview/Display/Surface/ContextualBarSurface.tsx new file mode 100644 index 000000000000..e7cbc469e19c --- /dev/null +++ b/packages/uikit-playground/src/Components/Preview/Display/Surface/ContextualBarSurface.tsx @@ -0,0 +1,67 @@ +import { + Avatar, + Box, + Button, + ButtonGroup, + Contextualbar, + ContextualbarAction, + ContextualbarFooter, + ContextualbarHeader, + ContextualbarTitle, + Margins, +} from '@rocket.chat/fuselage'; +import { Scrollbars } from 'rc-scrollbars'; +import { useLayoutSizes } from '@rocket.chat/ui-contexts'; + +import DraggableList from '../../../Draggable/DraggableList'; +import type { DraggableListProps } from '../../../Draggable/DraggableList'; + +const ContextualBarSurface = ({ blocks, onDragEnd }: DraggableListProps) => ( + <Contextualbar height='100%' width={useLayoutSizes().contextualBar} position='absolute'> + <ContextualbarHeader> + <Avatar url='data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==' /> + <ContextualbarTitle>{'Contextual Bar'}</ContextualbarTitle> + <ContextualbarAction data-qa='ContextualbarActionClose' title='Close' name='cross' /> + </ContextualbarHeader> + + <Box height='100%' p='12px'> + <Box + height='100%' + display='flex' + flexShrink={1} + flexDirection='column' + flexGrow={1} + overflow='hidden' + > + <Scrollbars + autoHide + autoHideTimeout={2000} + autoHideDuration={500} + style={{ + width: '100%', + height: '100%', + flexGrow: 1, + willChange: 'transform', + overflowY: 'hidden', + }} + renderThumbVertical={({ style, ...props }): JSX.Element => ( + <div {...props} style={{ ...style, backgroundColor: 'rgba(0, 0, 0, 0.5)', borderRadius: '7px' }} /> + )} + > + <Margins blockEnd='x16'> + <DraggableList surface={3} blocks={blocks} onDragEnd={onDragEnd} /> + </Margins> + </Scrollbars> + </Box> + </Box> + + <ContextualbarFooter> + <ButtonGroup stretch> + <Button>Cancel</Button> + <Button primary>Submit</Button> + </ButtonGroup> + </ContextualbarFooter> + </Contextualbar> +); + +export default ContextualBarSurface; diff --git a/packages/uikit-playground/src/Components/Preview/Display/Surface/Surface.tsx b/packages/uikit-playground/src/Components/Preview/Display/Surface/Surface.tsx index 5c21fe2d36e2..1022dad93863 100644 --- a/packages/uikit-playground/src/Components/Preview/Display/Surface/Surface.tsx +++ b/packages/uikit-playground/src/Components/Preview/Display/Surface/Surface.tsx @@ -8,6 +8,7 @@ import type { Block } from '../../../Draggable/DraggableList'; import BannerSurface from './BannerSurface'; import MessageSurface from './MessageSurface'; import ModalSurface from './ModalSurface'; +import ContextualBarSurface from './ContextualBarSurface'; import { reorder } from './Reorder'; const Surface: FC = () => { @@ -39,6 +40,7 @@ const Surface: FC = () => { '1': () => <MessageSurface blocks={uniqueBlocks} onDragEnd={onDragEnd} />, '2': () => <BannerSurface blocks={uniqueBlocks} onDragEnd={onDragEnd} />, '3': () => <ModalSurface blocks={uniqueBlocks} onDragEnd={onDragEnd} />, + '4': () => <ContextualBarSurface blocks={uniqueBlocks} onDragEnd={onDragEnd}/>, }; return ( <Box pb='40px' pi='x20'> diff --git a/packages/uikit-playground/src/Components/SurfaceSelect/options.ts b/packages/uikit-playground/src/Components/SurfaceSelect/options.ts index 8fdf49677cfd..315b864a8feb 100644 --- a/packages/uikit-playground/src/Components/SurfaceSelect/options.ts +++ b/packages/uikit-playground/src/Components/SurfaceSelect/options.ts @@ -4,6 +4,7 @@ const options: SelectOption[] = [ ['1', 'Message Preview'], ['2', 'Banner Preview'], ['3', 'Modal Preview'], + ['4', 'ContextualBar Preview'], ]; export default options; diff --git a/yarn.lock b/yarn.lock index b3fc2efa9391..8cc8eada135c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11021,6 +11021,7 @@ __metadata: eslint-plugin-react-hooks: ^4.6.0 eslint-plugin-react-refresh: ^0.4.1 eslint4b-prebuilt: ^6.7.2 + rc-scrollbars: ^1.1.6 react: ^17.0.2 react-beautiful-dnd: ^13.1.1 react-dom: ^17.0.2 @@ -34530,6 +34531,19 @@ __metadata: languageName: node linkType: hard +"rc-scrollbars@npm:^1.1.6": + version: 1.1.6 + resolution: "rc-scrollbars@npm:1.1.6" + dependencies: + dom-css: ^2.1.0 + raf: ^3.4.1 + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 6d511fc14c1f932319ecef13de78e64453af349b2eec962b4d6f515e4bca4ca1b5555b6b875c1b5cae193b15069aff16cd7fa02550084b0e58bbeb398f74b80a + languageName: node + linkType: hard + "rc@npm:^1.0.1, rc@npm:^1.1.6, rc@npm:^1.2.7": version: 1.2.8 resolution: "rc@npm:1.2.8" From ef4dcbd3da3d89b9c287105f2043a94d908a7cdf Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Wed, 5 Jul 2023 13:40:05 -0300 Subject: [PATCH 062/149] chore: Add missing migration to index (#29728) --- apps/meteor/server/startup/migrations/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/meteor/server/startup/migrations/index.ts b/apps/meteor/server/startup/migrations/index.ts index 17f4c47bbc1c..f85acdf5aa93 100644 --- a/apps/meteor/server/startup/migrations/index.ts +++ b/apps/meteor/server/startup/migrations/index.ts @@ -33,4 +33,5 @@ import './v296'; import './v297'; import './v298'; import './v299'; +import './v300'; import './xrun'; From d45310c17492a01894c4fc790092cbaa880c4fe1 Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Wed, 5 Jul 2023 10:40:36 -0600 Subject: [PATCH 063/149] chore: `migrations` folder to `arch` team (#29729) --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bfc726ac2b43..2ad64fe9cb96 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -20,6 +20,7 @@ /apps/meteor/app/sms @RocketChat/omnichannel /apps/meteor/server @RocketChat/backend /apps/meteor/server/models @RocketChat/Architecture +apps/meteor/server/startup/migrations @RocketChat/Architecture /apps/meteor/packages/rocketchat-livechat @RocketChat/omnichannel /apps/meteor/server/services/voip @RocketChat/omnichannel /apps/meteor/server/services/omnichannel-voip @RocketChat/omnichannel From c39be5717bb99462bf9c08f099357508c6d229dc Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento <rodrigoknascimento@gmail.com> Date: Mon, 3 Jul 2023 18:21:35 -0300 Subject: [PATCH 064/149] chore: Improve performance of getActiveLocalUserCount (#29681) Co-authored-by: Diego Sampaio <chinello@gmail.com> --- apps/meteor/server/models/raw/Users.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 7cb839e8fcc4..b48d367dc629 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -2794,19 +2794,17 @@ export class UsersRaw extends BaseRaw { // here getActiveLocalUserCount() { return Promise.all([ + // Count all active users (fast based on index) this.col.countDocuments({ active: true, - type: { - $nin: ['app'], - }, - roles: { $ne: ['guest'] }, }), - this.col.countDocuments({ federated: true, active: true }), + // Count all active that are guests, apps or federated + // Fast based on indexes, usually based on guest index as is usually small this.col.countDocuments({ - isRemote: true, active: true, - roles: { $ne: ['guest'] }, + $or: [{ roles: ['guest'] }, { type: 'app' }, { federated: true }, { isRemote: true }], }), + // Get all active and remove the guests, apps, federated, etc ]).then((results) => results.reduce((a, b) => a - b)); } From 154bafc424e9d7a4c78394f4850634657e314084 Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Tue, 4 Jul 2023 17:54:45 -0300 Subject: [PATCH 065/149] chore: throttle exceptions counter increment (#29719) --- .../server/lib/RocketChat.ErrorHandler.js | 7 ++++++- apps/meteor/app/lib/server/lib/meteorFixes.js | 10 ++++++++-- apps/meteor/ee/server/startup/seatsCap.ts | 5 +++-- apps/meteor/lib/utils/throttledCounter.ts | 20 +++++++++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 apps/meteor/lib/utils/throttledCounter.ts diff --git a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js index 213d51c991c4..aac5566ffeae 100644 --- a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js +++ b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.js @@ -3,6 +3,11 @@ import { Settings, Users, Rooms } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { sendMessage } from '../../../lib/server'; +import { throttledCounter } from '../../../../lib/utils/throttledCounter'; + +const incException = throttledCounter((counter) => { + Settings.incrementValueById('Uncaught_Exceptions_Count', counter).catch(console.error); +}, 10000); class ErrorHandler { constructor() { @@ -31,7 +36,7 @@ class ErrorHandler { async registerHandlers() { process.on('uncaughtException', async (error) => { - await Settings.incrementValueById('Uncaught_Exceptions_Count'); + incException(); if (!this.reporting) { return; } diff --git a/apps/meteor/app/lib/server/lib/meteorFixes.js b/apps/meteor/app/lib/server/lib/meteorFixes.js index 0d7af6675f9a..7d298729be54 100644 --- a/apps/meteor/app/lib/server/lib/meteorFixes.js +++ b/apps/meteor/app/lib/server/lib/meteorFixes.js @@ -2,6 +2,8 @@ import { Meteor } from 'meteor/meteor'; import { MongoInternals } from 'meteor/mongo'; import { Settings } from '@rocket.chat/models'; +import { throttledCounter } from '../../../../lib/utils/throttledCounter'; + const timeoutQuery = parseInt(process.env.OBSERVERS_CHECK_TIMEOUT) || 2 * 60 * 1000; const interval = parseInt(process.env.OBSERVERS_CHECK_INTERVAL) || 60 * 1000; const debug = Boolean(process.env.OBSERVERS_CHECK_DEBUG); @@ -45,6 +47,10 @@ Meteor.setInterval(() => { }); }, interval); +const incException = throttledCounter((counter) => { + Settings.incrementValueById('Uncaught_Exceptions_Count', counter).catch(console.error); +}, 10000); + /** * If some promise is rejected and doesn't have a catch (unhandledRejection) it may cause this finally * here https://github.com/meteor/meteor/blob/be6e529a739f47446950e045f4547ee60e5de7ae/packages/mongo/oplog_tailing.js#L348 @@ -59,8 +65,8 @@ Meteor.setInterval(() => { * we will start respecting this and exit the process to prevent these kind of problems. */ -process.on('unhandledRejection', async (error) => { - await Settings.incrementValueById('Uncaught_Exceptions_Count'); +process.on('unhandledRejection', (error) => { + incException(); console.error('=== UnHandledPromiseRejection ==='); console.error(error); diff --git a/apps/meteor/ee/server/startup/seatsCap.ts b/apps/meteor/ee/server/startup/seatsCap.ts index fcb643780504..f03164f89ab0 100644 --- a/apps/meteor/ee/server/startup/seatsCap.ts +++ b/apps/meteor/ee/server/startup/seatsCap.ts @@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; +import { throttle } from 'underscore'; import { callbacks } from '../../../lib/callbacks'; import { canAddNewUser, getMaxActiveUsers, onValidateLicenses } from '../../app/license/server/license'; @@ -79,7 +80,7 @@ callbacks.add( 'check-max-user-seats', ); -async function handleMaxSeatsBanners() { +const handleMaxSeatsBanners = throttle(async function _handleMaxSeatsBanners() { const maxActiveUsers = getMaxActiveUsers(); if (!maxActiveUsers) { @@ -106,7 +107,7 @@ async function handleMaxSeatsBanners() { } else { await enableDangerBanner(); } -} +}, 10000); callbacks.add('afterCreateUser', handleMaxSeatsBanners, callbacks.priority.MEDIUM, 'handle-max-seats-banners'); diff --git a/apps/meteor/lib/utils/throttledCounter.ts b/apps/meteor/lib/utils/throttledCounter.ts new file mode 100644 index 000000000000..6580699b3630 --- /dev/null +++ b/apps/meteor/lib/utils/throttledCounter.ts @@ -0,0 +1,20 @@ +import { throttle } from 'underscore'; + +export function throttledCounter(fn: (counter: number) => unknown, wait: number) { + let counter = 0; + + const throttledFn = throttle( + () => { + fn(counter); + + counter = 0; + }, + wait, + { leading: false }, + ); + + return () => { + counter++; + throttledFn(); + }; +} From e6b4e3401821df063dce06d80f721fefbaf8c0ea Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Tue, 4 Jul 2023 18:21:00 -0300 Subject: [PATCH 066/149] chore: device management improve readability and performance (#29712) --- .../ee/server/lib/deviceManagement/session.ts | 160 +++++++++--------- .../ee/server/startup/deviceManagement.ts | 7 +- apps/meteor/server/models/raw/Users.js | 10 ++ .../model-typings/src/models/IUsersModel.ts | 1 + 4 files changed, 101 insertions(+), 77 deletions(-) diff --git a/apps/meteor/ee/server/lib/deviceManagement/session.ts b/apps/meteor/ee/server/lib/deviceManagement/session.ts index d386e8798476..1596fbe450e4 100644 --- a/apps/meteor/ee/server/lib/deviceManagement/session.ts +++ b/apps/meteor/ee/server/lib/deviceManagement/session.ts @@ -1,14 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { UAParser } from 'ua-parser-js'; -import type { ISocketConnection, IUser } from '@rocket.chat/core-typings'; +import type { ISocketConnection } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import * as Mailer from '../../../../app/mailer/server/api'; import { settings } from '../../../../app/settings/server'; import { UAParserDesktop, UAParserMobile } from '../../../../app/statistics/server/lib/UAParserCustom'; import { deviceManagementEvents } from '../../../../server/services/device-management/events'; -import { hasLicense } from '../../../app/license/server/license'; import { t, getUserPreference } from '../../../../app/utils/server'; let mailTemplates: string; @@ -22,88 +21,97 @@ Meteor.startup(() => { const uaParser = async ( uaString: ISocketConnection['httpHeaders']['user-agent'], ): Promise<UAParser.IResult & { app?: { name: string; version: string; bundle: string } }> => { - let rcAgent = {}; - if (uaString && UAParserMobile.isMobileApp(uaString)) { - rcAgent = UAParserMobile.uaObject(uaString); - } - - if (uaString && UAParserDesktop.isDesktopApp(uaString)) { - rcAgent = UAParserDesktop.uaObject(uaString); - } - const ua = new UAParser(uaString); - return { ...ua.getResult(), ...rcAgent }; + return { + ...ua.getResult(), + ...(uaString && UAParserMobile.isMobileApp(uaString) && UAParserMobile.uaObject(uaString)), + ...(uaString && UAParserDesktop.isDesktopApp(uaString) && UAParserDesktop.uaObject(uaString)), + }; }; -export const listenSessionLogin = async (): Promise<void> => { - deviceManagementEvents.on('device-login', async ({ userId, connection }) => { - if (!hasLicense('device-management')) return; +export const listenSessionLogin = () => { + return deviceManagementEvents.on('device-login', async ({ userId, connection }) => { + const deviceEnabled = settings.get('Device_Management_Enable_Login_Emails'); + + if (!deviceEnabled) { + return; + } + + if (connection.loginToken) { + return; + } + + const user = await Users.findOneByIdWithEmailAddress(userId, { + projection: { 'name': 1, 'username': 1, 'emails': 1, 'settings.preferences.receiveLoginDetectionEmail': 1 }, + }); + + if (!user?.emails?.length) { + return; + } + + const userReceiveLoginEmailPreference = + !settings.get('Device_Management_Allow_Login_Email_preference') || + (await getUserPreference(userId, 'receiveLoginDetectionEmail', true)); - const user = await Users.findOneById<IUser>(userId, { projection: { name: 1, username: 1, emails: 1 } }); - if (user?.emails?.length && !connection.loginToken) { - const { - name, - username, - emails: [{ address: email }], - } = user; - const { browser, os, device, cpu, app } = await uaParser(connection.httpHeaders['user-agent']); + if (!userReceiveLoginEmailPreference) { + return; + } - const mailData = { - name, - username, - browserInfo: `${browser.name} ${browser.version}`, - osInfo: `${os.name}`, - deviceInfo: `${device.type || t('Device_Management_Device_Unknown')} ${device.vendor || ''} ${device.model || ''} ${ - cpu.architecture || '' - }`, - ipInfo: connection.clientAddress, - userAgent: '', - }; + const { + name, + username, + emails: [{ address: email }], + } = user; + const { browser, os, device, cpu, app } = await uaParser(connection.httpHeaders['user-agent']); - switch (device.type) { - case 'mobile': - case 'tablet': - case 'smarttv': - mailData.browserInfo = `${browser.name} ${browser.version}`; - mailData.osInfo = `${os.name}`; - mailData.deviceInfo = `${device.type} ${device.vendor || ''} ${device.model || ''} ${cpu.architecture || ''}`; - break; - case 'mobile-app': - mailData.browserInfo = `Rocket.Chat App ${app?.bundle || app?.version}`; - mailData.osInfo = `${os.name}`; - mailData.deviceInfo = 'Mobile App'; - break; - case 'desktop-app': - mailData.browserInfo = `Rocket.Chat ${app?.name || browser.name} ${app?.bundle || app?.version || browser.version}`; - mailData.osInfo = `${os.name}`; - mailData.deviceInfo = `Desktop App ${cpu.architecture || ''}`; - break; - default: - mailData.userAgent = connection.httpHeaders['user-agent'] || ''; - break; - } + const mailData = { + name, + username, + browserInfo: `${browser.name} ${browser.version}`, + osInfo: `${os.name}`, + deviceInfo: `${device.type || t('Device_Management_Device_Unknown')} ${device.vendor || ''} ${device.model || ''} ${ + cpu.architecture || '' + }`, + ipInfo: connection.clientAddress, + userAgent: '', + }; - try { - const userReceiveLoginEmailPreference = settings.get('Device_Management_Allow_Login_Email_preference') - ? await getUserPreference(userId, 'receiveLoginDetectionEmail', true) - : true; - const shouldSendLoginEmail = settings.get('Device_Management_Enable_Login_Emails') && userReceiveLoginEmailPreference; + switch (device.type) { + case 'mobile': + case 'tablet': + case 'smarttv': + mailData.browserInfo = `${browser.name} ${browser.version}`; + mailData.osInfo = `${os.name}`; + mailData.deviceInfo = `${device.type} ${device.vendor || ''} ${device.model || ''} ${cpu.architecture || ''}`; + break; + case 'mobile-app': + mailData.browserInfo = `Rocket.Chat App ${app?.bundle || app?.version}`; + mailData.osInfo = `${os.name}`; + mailData.deviceInfo = 'Mobile App'; + break; + case 'desktop-app': + mailData.browserInfo = `Rocket.Chat ${app?.name || browser.name} ${app?.bundle || app?.version || browser.version}`; + mailData.osInfo = `${os.name}`; + mailData.deviceInfo = `Desktop App ${cpu.architecture || ''}`; + break; + default: + mailData.userAgent = connection.httpHeaders['user-agent'] || ''; + break; + } - if (shouldSendLoginEmail) { - await Mailer.send({ - to: `${name} <${email}>`, - from: Accounts.emailTemplates.from, - subject: settings.get('Device_Management_Email_Subject'), - html: mailTemplates, - data: mailData, - }); - } - } catch ({ message }: any) { - throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${message}`, { - method: 'listenSessionLogin', - message, - }); - } + try { + await Mailer.send({ + to: `${name} <${email}>`, + from: Accounts.emailTemplates.from, + subject: settings.get('Device_Management_Email_Subject'), + html: mailTemplates, + data: mailData, + }); + } catch ({ message }: any) { + throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${message}`, { + method: 'listenSessionLogin', + message, + }); } }); }; diff --git a/apps/meteor/ee/server/startup/deviceManagement.ts b/apps/meteor/ee/server/startup/deviceManagement.ts index cb1c782ead92..6e6b0449df3e 100644 --- a/apps/meteor/ee/server/startup/deviceManagement.ts +++ b/apps/meteor/ee/server/startup/deviceManagement.ts @@ -1,6 +1,7 @@ import { onToggledFeature } from '../../app/license/server/license'; import { addSettings } from '../settings/deviceManagement'; +let stopListening: (() => void) | undefined; onToggledFeature('device-management', { up: async () => { const { createPermissions, createEmailTemplates } = await import('../lib/deviceManagement/startup'); @@ -9,6 +10,10 @@ onToggledFeature('device-management', { addSettings(); await createPermissions(); await createEmailTemplates(); - await listenSessionLogin(); + stopListening = await listenSessionLogin(); + }, + down: async () => { + stopListening?.(); + stopListening = undefined; }, }); diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index b48d367dc629..423d4a627891 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1243,6 +1243,16 @@ export class UsersRaw extends BaseRaw { return this.findOne({ 'services.password.reset.token': token }, options); } + findOneByIdWithEmailAddress(userId, options) { + return this.findOne( + { + _id: userId, + emails: { $exists: true, $ne: [] }, + }, + options, + ); + } + setFederationAvatarUrlById(userId, federationAvatarUrl) { return this.updateOne( { diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 80619d394238..c8a5f2617cdc 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -16,6 +16,7 @@ export interface IUsersModel extends IBaseModel<IUser> { addRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][]): Promise<UpdateResult>; findUsersInRoles<T = IUser>(roles: IRole['_id'][], scope?: null, options?: any): FindCursor<T>; findPaginatedUsersInRoles<T = IUser>(roles: IRole['_id'][], options?: any): FindPaginated<FindCursor<T>>; + findOneByIdWithEmailAddress(uid: IUser['_id'], options?: FindOptions<IUser>): Promise<IUser | null>; findOneByUsername<T = IUser>(username: string, options?: any): Promise<T>; findOneAgentById<T = ILivechatAgent>(_id: string, options: any): Promise<T>; findUsersInRolesWithQuery<T = IUser>(roles: IRole['_id'] | IRole['_id'][], query: any, options: any): FindCursor<T>; From 2f0f67f3135028496a7894b81674985e58cb5a8c Mon Sep 17 00:00:00 2001 From: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Date: Tue, 4 Jul 2023 20:59:53 -0300 Subject: [PATCH 067/149] fix(meteor): `room-opened` event not dispatching when navigating cached rooms (#29718) --- .changeset/hip-hornets-fail.md | 5 +++++ apps/meteor/client/views/room/hooks/useOpenRoom.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .changeset/hip-hornets-fail.md diff --git a/.changeset/hip-hornets-fail.md b/.changeset/hip-hornets-fail.md new file mode 100644 index 000000000000..5bd0c0d15199 --- /dev/null +++ b/.changeset/hip-hornets-fail.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed `room-opened` event not dispatching when navigating cached rooms diff --git a/apps/meteor/client/views/room/hooks/useOpenRoom.ts b/apps/meteor/client/views/room/hooks/useOpenRoom.ts index 8b9f5fcdf75a..288767ccde53 100644 --- a/apps/meteor/client/views/room/hooks/useOpenRoom.ts +++ b/apps/meteor/client/views/room/hooks/useOpenRoom.ts @@ -1,6 +1,7 @@ import type { IRoom, RoomType } from '@rocket.chat/core-typings'; import { useMethod, useRoute, useSetting, useUser } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; +import { useRef } from 'react'; import { ChatRoom, ChatSubscription } from '../../../../app/models/client'; import { LegacyRoomManager } from '../../../../app/ui-utils/client'; @@ -20,6 +21,8 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st const openRoom = useMethod('openRoom'); const directRoute = useRoute('direct'); + const unsubscribeFromRoomOpenedEvent = useRef<() => void>(() => undefined); + return useQuery( // we need to add uid and username here because `user` is not loaded all at once (see UserProvider -> Meteor.user()) ['rooms', { type, reference }, { uid: user?._id, username: user?.username }] as const, @@ -63,14 +66,15 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st throw new RoomNotFoundError(undefined, { rid: room._id }); } + unsubscribeFromRoomOpenedEvent.current(); + unsubscribeFromRoomOpenedEvent.current = RoomManager.once('opened', () => fireGlobalEvent('room-opened', omit(room, 'usernames'))); + LegacyRoomManager.open({ typeName: type + reference, rid: room._id }); if (room._id === RoomManager.opened) { return { rid: room._id }; } - fireGlobalEvent('room-opened', omit(room, 'usernames')); - // update user's room subscription const sub = ChatSubscription.findOne({ rid: room._id }); if (sub && !sub.open) { From 1ffc60dfd653d760894043fdf94d47ca9c0405a1 Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Wed, 5 Jul 2023 14:27:31 -0300 Subject: [PATCH 068/149] Revert "Release 6.2.9" This reverts commit 8a268255f01d758094077ca17e48e6fa8119e26f. --- .changeset/hip-comics-drop.md | 2 ++ .changeset/itchy-hotels-care.md | 5 ++++ .changeset/soft-carrots-add.md | 5 ++++ apps/meteor/.docker/Dockerfile.rhel | 2 +- apps/meteor/CHANGELOG.md | 27 ------------------- apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/ee/server/services/CHANGELOG.md | 11 -------- apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/CHANGELOG.md | 11 -------- ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/CHANGELOG.md | 11 -------- ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/CHANGELOG.md | 12 --------- ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/CHANGELOG.md | 12 --------- ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/CHANGELOG.md | 11 -------- ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/CHANGELOG.md | 11 -------- ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/CHANGELOG.md | 10 ------- ee/apps/stream-hub-service/package.json | 2 +- ee/packages/omnichannel-services/CHANGELOG.md | 12 --------- ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/CHANGELOG.md | 7 ----- ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/CHANGELOG.md | 9 ------- ee/packages/presence/package.json | 2 +- package.json | 2 +- packages/api-client/CHANGELOG.md | 8 ------ packages/api-client/package.json | 2 +- packages/core-services/CHANGELOG.md | 9 ------- packages/core-services/package.json | 2 +- packages/core-typings/CHANGELOG.md | 2 -- packages/core-typings/package.json | 2 +- packages/instance-status/CHANGELOG.md | 7 ----- packages/instance-status/package.json | 2 +- packages/model-typings/CHANGELOG.md | 7 ----- packages/model-typings/package.json | 2 +- packages/models/CHANGELOG.md | 7 ----- packages/models/package.json | 2 +- packages/rest-typings/CHANGELOG.md | 7 ----- packages/rest-typings/package.json | 2 +- packages/ui-contexts/CHANGELOG.md | 8 ------ packages/ui-contexts/package.json | 2 +- 46 files changed, 35 insertions(+), 222 deletions(-) create mode 100644 .changeset/hip-comics-drop.md create mode 100644 .changeset/itchy-hotels-care.md create mode 100644 .changeset/soft-carrots-add.md diff --git a/.changeset/hip-comics-drop.md b/.changeset/hip-comics-drop.md new file mode 100644 index 000000000000..a845151cc840 --- /dev/null +++ b/.changeset/hip-comics-drop.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/itchy-hotels-care.md b/.changeset/itchy-hotels-care.md new file mode 100644 index 000000000000..93aed3ed6dce --- /dev/null +++ b/.changeset/itchy-hotels-care.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed video message button disabled on iOS browsers diff --git a/.changeset/soft-carrots-add.md b/.changeset/soft-carrots-add.md new file mode 100644 index 000000000000..80aba278f3a8 --- /dev/null +++ b/.changeset/soft-carrots-add.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fixed an error on mobile ios browser where if you started recording audio and denied permission, it would look like it is still recording diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index 583b013d274b..e3746d22c0b0 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 6.2.9 +ENV RC_VERSION 6.2.8 MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index 011b7562f890..c27531b45111 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,32 +1,5 @@ # @rocket.chat/meteor -## 6.2.9 - -### Patch Changes - -- [#29686](https://github.com/RocketChat/Rocket.Chat/pull/29686) [`b1d103b1d5`](https://github.com/RocketChat/Rocket.Chat/commit/b1d103b1d58a4b67595b7fff2404becc2145b1a3) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - fixed video message button disabled on iOS browsers - -- [#29686](https://github.com/RocketChat/Rocket.Chat/pull/29686) [`a6115380fa`](https://github.com/RocketChat/Rocket.Chat/commit/a6115380fa49bc4cebdf749bba3644d08a89064e) Thanks [@rocketchat-github-ci](https://github.com/rocketchat-github-ci)! - fixed an error on mobile ios browser where if you started recording audio and denied permission, it would look like it is still recording - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - - @rocket.chat/omnichannel-services@0.0.4 - - @rocket.chat/pdf-worker@0.0.4 - - @rocket.chat/presence@0.0.4 - - @rocket.chat/api-client@0.0.4 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/gazzodown@0.0.1 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/ui-contexts@0.0.4 - - @rocket.chat/models@0.0.4 - - @rocket.chat/ui-theming@0.0.1 - - @rocket.chat/fuselage-ui-kit@0.31.16 - - @rocket.chat/ui-client@0.0.1 - - @rocket.chat/ui-video-conf@0.0.1 - - @rocket.chat/web-ui-registration@0.0.1 - - @rocket.chat/instance-status@0.0.4 - ## 6.2.7 ### Patch Changes diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index 2f6cc0c285a4..ab44536c9ebe 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.2.9" + "version": "6.2.8" } diff --git a/apps/meteor/ee/server/services/CHANGELOG.md b/apps/meteor/ee/server/services/CHANGELOG.md index a89b8f622d4b..e0e9d1ca5c11 100644 --- a/apps/meteor/ee/server/services/CHANGELOG.md +++ b/apps/meteor/ee/server/services/CHANGELOG.md @@ -1,16 +1,5 @@ # rocketchat-services -## 1.0.3 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 1.0.2 ### Patch Changes diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index 8b0123178d4c..72ac960e626a 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -1,7 +1,7 @@ { "name": "rocketchat-services", "private": true, - "version": "1.0.3", + "version": "1.0.2", "description": "Rocket.Chat Authorization service", "main": "index.js", "scripts": { diff --git a/apps/meteor/package.json b/apps/meteor/package.json index d64375f7e5d8..b756e6f7d981 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/meteor", "description": "The Ultimate Open Source WebChat Platform", - "version": "6.2.9", + "version": "6.2.8", "private": true, "author": { "name": "Rocket.Chat", diff --git a/ee/apps/account-service/CHANGELOG.md b/ee/apps/account-service/CHANGELOG.md index d978f4e9db2f..1fb9d208707d 100644 --- a/ee/apps/account-service/CHANGELOG.md +++ b/ee/apps/account-service/CHANGELOG.md @@ -1,16 +1,5 @@ # @rocket.chat/account-service -## 0.1.3 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.1.2 ### Patch Changes diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index 5e90de6201b9..29f091a2115d 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/account-service", "private": true, - "version": "0.1.3", + "version": "0.1.2", "description": "Rocket.Chat Account service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/authorization-service/CHANGELOG.md b/ee/apps/authorization-service/CHANGELOG.md index e586e5e8d60e..815e805c9cb3 100644 --- a/ee/apps/authorization-service/CHANGELOG.md +++ b/ee/apps/authorization-service/CHANGELOG.md @@ -1,16 +1,5 @@ # @rocket.chat/authorization-service -## 0.1.3 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.1.2 ### Patch Changes diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index ae4e12d76d39..d897508cfe38 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/authorization-service", "private": true, - "version": "0.1.3", + "version": "0.1.2", "description": "Rocket.Chat Authorization service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/ddp-streamer/CHANGELOG.md b/ee/apps/ddp-streamer/CHANGELOG.md index 29187bf9f2b2..7b94c4927772 100644 --- a/ee/apps/ddp-streamer/CHANGELOG.md +++ b/ee/apps/ddp-streamer/CHANGELOG.md @@ -1,17 +1,5 @@ # @rocket.chat/ddp-streamer -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - - @rocket.chat/instance-status@0.0.4 - ## 0.0.3 ### Patch Changes diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index 9caa8ceabace..f10c8ea3b757 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/ddp-streamer", "private": true, - "version": "0.0.4", + "version": "0.0.3", "description": "Rocket.Chat DDP-Streamer service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/omnichannel-transcript/CHANGELOG.md b/ee/apps/omnichannel-transcript/CHANGELOG.md index f855a14d51b0..9d9a9acc118b 100644 --- a/ee/apps/omnichannel-transcript/CHANGELOG.md +++ b/ee/apps/omnichannel-transcript/CHANGELOG.md @@ -1,17 +1,5 @@ # @rocket.chat/omnichannel-transcript -## 0.1.3 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/omnichannel-services@0.0.4 - - @rocket.chat/pdf-worker@0.0.4 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.1.2 ### Patch Changes diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index 3503f8b5b6f7..d99b89d16e42 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/omnichannel-transcript", "private": true, - "version": "0.1.3", + "version": "0.1.2", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/presence-service/CHANGELOG.md b/ee/apps/presence-service/CHANGELOG.md index d0f5b777a517..8891187ff08f 100644 --- a/ee/apps/presence-service/CHANGELOG.md +++ b/ee/apps/presence-service/CHANGELOG.md @@ -1,16 +1,5 @@ # @rocket.chat/presence-service -## 0.1.3 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/presence@0.0.4 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.1.2 ### Patch Changes diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index ec56bc6c49c2..9cccd3703f3e 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/presence-service", "private": true, - "version": "0.1.3", + "version": "0.1.2", "description": "Rocket.Chat Presence service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/queue-worker/CHANGELOG.md b/ee/apps/queue-worker/CHANGELOG.md index 53ef8de2e587..08b199f14d9c 100644 --- a/ee/apps/queue-worker/CHANGELOG.md +++ b/ee/apps/queue-worker/CHANGELOG.md @@ -1,16 +1,5 @@ # @rocket.chat/queue-worker -## 0.1.3 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/omnichannel-services@0.0.4 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.1.2 ### Patch Changes diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index 7503f17157c0..efdfae6cd15b 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/queue-worker", "private": true, - "version": "0.1.3", + "version": "0.1.2", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/stream-hub-service/CHANGELOG.md b/ee/apps/stream-hub-service/CHANGELOG.md index 377e849b5cf8..de639f94831a 100644 --- a/ee/apps/stream-hub-service/CHANGELOG.md +++ b/ee/apps/stream-hub-service/CHANGELOG.md @@ -1,15 +1,5 @@ # @rocket.chat/stream-hub-service -## 0.1.3 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.1.2 ### Patch Changes diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index cc1a1686a940..3d481a9160b7 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/stream-hub-service", "private": true, - "version": "0.1.3", + "version": "0.1.2", "description": "Rocket.Chat Stream Hub service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/packages/omnichannel-services/CHANGELOG.md b/ee/packages/omnichannel-services/CHANGELOG.md index 291ac808eaa8..4b169eff955c 100644 --- a/ee/packages/omnichannel-services/CHANGELOG.md +++ b/ee/packages/omnichannel-services/CHANGELOG.md @@ -1,17 +1,5 @@ # @rocket.chat/omnichannel-services -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - - @rocket.chat/pdf-worker@0.0.4 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/model-typings@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.0.3 ### Patch Changes diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index 8e88592b2d1e..3eefbc9cd26a 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/omnichannel-services", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/ee/packages/pdf-worker/CHANGELOG.md b/ee/packages/pdf-worker/CHANGELOG.md index 1953495a9e43..bf44622d6166 100644 --- a/ee/packages/pdf-worker/CHANGELOG.md +++ b/ee/packages/pdf-worker/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/pdf-worker -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - ## 0.0.3 ### Patch Changes diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index be365fb5cb05..fab3db3504c0 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/pdf-worker", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@storybook/addon-actions": "~6.5.14", diff --git a/ee/packages/presence/CHANGELOG.md b/ee/packages/presence/CHANGELOG.md index 74e689dcefdf..bc882fd8ec0f 100644 --- a/ee/packages/presence/CHANGELOG.md +++ b/ee/packages/presence/CHANGELOG.md @@ -1,14 +1,5 @@ # @rocket.chat/presence -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/core-services@0.0.4 - - @rocket.chat/models@0.0.4 - ## 0.0.3 ### Patch Changes diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 9112b3daa68c..6894a268f110 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/presence", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@babel/core": "^7.20.5", diff --git a/package.json b/package.json index f12f83ec5e71..5e6cc689277d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.2.9", + "version": "6.2.8", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/api-client/CHANGELOG.md b/packages/api-client/CHANGELOG.md index 88b2916f94f0..6e3b931cfdab 100644 --- a/packages/api-client/CHANGELOG.md +++ b/packages/api-client/CHANGELOG.md @@ -1,13 +1,5 @@ # @rocket.chat/api-client -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - ## 0.0.3 ### Patch Changes diff --git a/packages/api-client/package.json b/packages/api-client/package.json index 168cc248689a..1fad6dbde4e0 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/api-client", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/core-services/CHANGELOG.md b/packages/core-services/CHANGELOG.md index 8434f1102487..f9f550c86fb5 100644 --- a/packages/core-services/CHANGELOG.md +++ b/packages/core-services/CHANGELOG.md @@ -1,14 +1,5 @@ # @rocket.chat/core-services -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - - @rocket.chat/models@0.0.4 - ## 0.0.3 ### Patch Changes diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 44417b19e782..a608d182e65d 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-services", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/core-typings/CHANGELOG.md b/packages/core-typings/CHANGELOG.md index 217cf6e00d2d..11526ef6d5a9 100644 --- a/packages/core-typings/CHANGELOG.md +++ b/packages/core-typings/CHANGELOG.md @@ -1,7 +1,5 @@ # @rocket.chat/core-typings -## 6.2.9 - ## 6.2.7 ## 6.2.6 diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 6895e7ac55ca..ce13876bb9f7 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-typings", - "version": "6.2.9", + "version": "6.2.8", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/instance-status/CHANGELOG.md b/packages/instance-status/CHANGELOG.md index 243f25c30227..e8726035358d 100644 --- a/packages/instance-status/CHANGELOG.md +++ b/packages/instance-status/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/instance-status -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/models@0.0.4 - ## 0.0.3 ### Patch Changes diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index 7002956ed343..1a9cbeb3967b 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/instance-status", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/model-typings/CHANGELOG.md b/packages/model-typings/CHANGELOG.md index 4f0e3b55aa22..71417ee3e9ef 100644 --- a/packages/model-typings/CHANGELOG.md +++ b/packages/model-typings/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/model-typings -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - ## 0.0.3 ### Patch Changes diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 690ffe0c21c1..312410396b9c 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/model-typings", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index 55191c41a078..87ccf5f8c057 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/models -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/model-typings@0.0.4 - ## 0.0.3 ### Patch Changes diff --git a/packages/models/package.json b/packages/models/package.json index d9ed6794d8ba..99909f53ec30 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/models", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/rest-typings/CHANGELOG.md b/packages/rest-typings/CHANGELOG.md index 3b192d977dc4..39debf6d6189 100644 --- a/packages/rest-typings/CHANGELOG.md +++ b/packages/rest-typings/CHANGELOG.md @@ -1,12 +1,5 @@ # @rocket.chat/rest-typings -## 6.2.9 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - ## 6.2.7 ### Patch Changes diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 57bcd3112dc3..63f5bca2464b 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.2.9", + "version": "6.2.8", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/ui-contexts/CHANGELOG.md b/packages/ui-contexts/CHANGELOG.md index fef4ff588905..465c0fad5029 100644 --- a/packages/ui-contexts/CHANGELOG.md +++ b/packages/ui-contexts/CHANGELOG.md @@ -1,13 +1,5 @@ # @rocket.chat/ui-contexts -## 0.0.4 - -### Patch Changes - -- Updated dependencies []: - - @rocket.chat/core-typings@6.2.9 - - @rocket.chat/rest-typings@6.2.9 - ## 0.0.3 ### Patch Changes diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 31793d37f3c0..123a092e2958 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-contexts", - "version": "0.0.4", + "version": "0.0.3", "private": true, "devDependencies": { "@rocket.chat/core-typings": "workspace:^", From abf746733b73a50126a6daeb5920c9a34fc30926 Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Wed, 5 Jul 2023 14:30:07 -0300 Subject: [PATCH 069/149] Release 6.2.9 --- .changeset/config.json | 2 +- .changeset/hip-comics-drop.md | 2 -- .changeset/hip-hornets-fail.md | 5 ---- .changeset/itchy-hotels-care.md | 5 ---- .changeset/soft-carrots-add.md | 5 ---- .github/workflows/publish-release.yml | 2 +- apps/meteor/.docker/Dockerfile.rhel | 2 +- apps/meteor/CHANGELOG.md | 25 +++++++++++++++++++ apps/meteor/app/utils/rocketchat.info | 2 +- apps/meteor/ee/server/services/CHANGELOG.md | 10 ++++++++ apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 2 +- ee/apps/account-service/CHANGELOG.md | 10 ++++++++ ee/apps/account-service/package.json | 2 +- ee/apps/authorization-service/CHANGELOG.md | 10 ++++++++ ee/apps/authorization-service/package.json | 2 +- ee/apps/ddp-streamer/CHANGELOG.md | 11 ++++++++ ee/apps/ddp-streamer/package.json | 2 +- ee/apps/omnichannel-transcript/CHANGELOG.md | 11 ++++++++ ee/apps/omnichannel-transcript/package.json | 2 +- ee/apps/presence-service/CHANGELOG.md | 10 ++++++++ ee/apps/presence-service/package.json | 2 +- ee/apps/queue-worker/CHANGELOG.md | 10 ++++++++ ee/apps/queue-worker/package.json | 2 +- ee/apps/stream-hub-service/CHANGELOG.md | 9 +++++++ ee/apps/stream-hub-service/package.json | 2 +- ee/packages/omnichannel-services/CHANGELOG.md | 11 ++++++++ ee/packages/omnichannel-services/package.json | 2 +- ee/packages/pdf-worker/CHANGELOG.md | 6 +++++ ee/packages/pdf-worker/package.json | 2 +- ee/packages/presence/CHANGELOG.md | 8 ++++++ ee/packages/presence/package.json | 2 +- package.json | 2 +- packages/api-client/CHANGELOG.md | 7 ++++++ packages/api-client/package.json | 2 +- packages/core-services/CHANGELOG.md | 8 ++++++ packages/core-services/package.json | 2 +- packages/core-typings/CHANGELOG.md | 2 ++ packages/core-typings/package.json | 2 +- packages/instance-status/CHANGELOG.md | 6 +++++ packages/instance-status/package.json | 2 +- packages/model-typings/CHANGELOG.md | 6 +++++ packages/model-typings/package.json | 2 +- packages/models/CHANGELOG.md | 6 +++++ packages/models/package.json | 2 +- .../release-action/src/bumpNextVersion.ts | 2 +- .../release-action/src/startPatchRelease.ts | 2 +- packages/rest-typings/CHANGELOG.md | 6 +++++ packages/rest-typings/package.json | 2 +- packages/ui-contexts/CHANGELOG.md | 7 ++++++ packages/ui-contexts/package.json | 2 +- 51 files changed, 206 insertions(+), 44 deletions(-) delete mode 100644 .changeset/hip-comics-drop.md delete mode 100644 .changeset/hip-hornets-fail.md delete mode 100644 .changeset/itchy-hotels-care.md delete mode 100644 .changeset/soft-carrots-add.md diff --git a/.changeset/config.json b/.changeset/config.json index 5dfd3f70619b..9d77693b3b31 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,6 +1,6 @@ { "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", - "changelog": ["@changesets/changelog-github", { "repo": "RocketChat/Rocket.Chat" }], + "changelog": "@changesets/changelog-git", "commit": false, "fixed": [ ["@rocket.chat/meteor", "@rocket.chat/core-typings", "@rocket.chat/rest-typings"] diff --git a/.changeset/hip-comics-drop.md b/.changeset/hip-comics-drop.md deleted file mode 100644 index a845151cc840..000000000000 --- a/.changeset/hip-comics-drop.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/hip-hornets-fail.md b/.changeset/hip-hornets-fail.md deleted file mode 100644 index 5bd0c0d15199..000000000000 --- a/.changeset/hip-hornets-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fixed `room-opened` event not dispatching when navigating cached rooms diff --git a/.changeset/itchy-hotels-care.md b/.changeset/itchy-hotels-care.md deleted file mode 100644 index 93aed3ed6dce..000000000000 --- a/.changeset/itchy-hotels-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fixed video message button disabled on iOS browsers diff --git a/.changeset/soft-carrots-add.md b/.changeset/soft-carrots-add.md deleted file mode 100644 index 80aba278f3a8..000000000000 --- a/.changeset/soft-carrots-add.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@rocket.chat/meteor": patch ---- - -fixed an error on mobile ios browser where if you started recording audio and denied permission, it would look like it is still recording diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 74112cb86fd8..3def1c819fb1 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -3,7 +3,7 @@ name: Publish Final Release on: push: branches: - - master + - release-automation concurrency: ${{ github.workflow }}-${{ github.ref }} diff --git a/apps/meteor/.docker/Dockerfile.rhel b/apps/meteor/.docker/Dockerfile.rhel index e3746d22c0b0..583b013d274b 100644 --- a/apps/meteor/.docker/Dockerfile.rhel +++ b/apps/meteor/.docker/Dockerfile.rhel @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi8/nodejs-12 -ENV RC_VERSION 6.2.8 +ENV RC_VERSION 6.2.9 MAINTAINER buildmaster@rocket.chat diff --git a/apps/meteor/CHANGELOG.md b/apps/meteor/CHANGELOG.md index c27531b45111..343b4a25c8cb 100644 --- a/apps/meteor/CHANGELOG.md +++ b/apps/meteor/CHANGELOG.md @@ -1,5 +1,30 @@ # @rocket.chat/meteor +## 6.2.9 + +### Patch Changes + +- 2f0f67f313: fixed `room-opened` event not dispatching when navigating cached rooms +- 1ffc60dfd6: fixed video message button disabled on iOS browsers +- 1ffc60dfd6: fixed an error on mobile ios browser where if you started recording audio and denied permission, it would look like it is still recording + - @rocket.chat/core-typings@6.2.9 + - @rocket.chat/rest-typings@6.2.9 + - @rocket.chat/omnichannel-services@0.0.4 + - @rocket.chat/pdf-worker@0.0.4 + - @rocket.chat/presence@0.0.4 + - @rocket.chat/api-client@0.0.4 + - @rocket.chat/core-services@0.0.4 + - @rocket.chat/gazzodown@0.0.1 + - @rocket.chat/model-typings@0.0.4 + - @rocket.chat/ui-contexts@0.0.4 + - @rocket.chat/models@0.0.4 + - @rocket.chat/ui-theming@0.0.1 + - @rocket.chat/fuselage-ui-kit@0.31.16 + - @rocket.chat/ui-client@0.0.1 + - @rocket.chat/ui-video-conf@0.0.1 + - @rocket.chat/web-ui-registration@0.0.1 + - @rocket.chat/instance-status@0.0.4 + ## 6.2.7 ### Patch Changes diff --git a/apps/meteor/app/utils/rocketchat.info b/apps/meteor/app/utils/rocketchat.info index ab44536c9ebe..2f6cc0c285a4 100644 --- a/apps/meteor/app/utils/rocketchat.info +++ b/apps/meteor/app/utils/rocketchat.info @@ -1,3 +1,3 @@ { - "version": "6.2.8" + "version": "6.2.9" } diff --git a/apps/meteor/ee/server/services/CHANGELOG.md b/apps/meteor/ee/server/services/CHANGELOG.md index e0e9d1ca5c11..18e038bdb52a 100644 --- a/apps/meteor/ee/server/services/CHANGELOG.md +++ b/apps/meteor/ee/server/services/CHANGELOG.md @@ -1,5 +1,15 @@ # rocketchat-services +## 1.0.3 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 1.0.2 ### Patch Changes diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index 72ac960e626a..8b0123178d4c 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -1,7 +1,7 @@ { "name": "rocketchat-services", "private": true, - "version": "1.0.2", + "version": "1.0.3", "description": "Rocket.Chat Authorization service", "main": "index.js", "scripts": { diff --git a/apps/meteor/package.json b/apps/meteor/package.json index b756e6f7d981..d64375f7e5d8 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/meteor", "description": "The Ultimate Open Source WebChat Platform", - "version": "6.2.8", + "version": "6.2.9", "private": true, "author": { "name": "Rocket.Chat", diff --git a/ee/apps/account-service/CHANGELOG.md b/ee/apps/account-service/CHANGELOG.md index 1fb9d208707d..048ceb52226c 100644 --- a/ee/apps/account-service/CHANGELOG.md +++ b/ee/apps/account-service/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/account-service +## 0.1.3 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index 29f091a2115d..5e90de6201b9 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/account-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Account service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/authorization-service/CHANGELOG.md b/ee/apps/authorization-service/CHANGELOG.md index 815e805c9cb3..60c4fc2f8b02 100644 --- a/ee/apps/authorization-service/CHANGELOG.md +++ b/ee/apps/authorization-service/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/authorization-service +## 0.1.3 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index d897508cfe38..ae4e12d76d39 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/authorization-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Authorization service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/ddp-streamer/CHANGELOG.md b/ee/apps/ddp-streamer/CHANGELOG.md index 7b94c4927772..8cf403f13b25 100644 --- a/ee/apps/ddp-streamer/CHANGELOG.md +++ b/ee/apps/ddp-streamer/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/ddp-streamer +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 +- @rocket.chat/instance-status@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index f10c8ea3b757..9caa8ceabace 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/ddp-streamer", "private": true, - "version": "0.0.3", + "version": "0.0.4", "description": "Rocket.Chat DDP-Streamer service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/omnichannel-transcript/CHANGELOG.md b/ee/apps/omnichannel-transcript/CHANGELOG.md index 9d9a9acc118b..c3af04eda860 100644 --- a/ee/apps/omnichannel-transcript/CHANGELOG.md +++ b/ee/apps/omnichannel-transcript/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/omnichannel-transcript +## 0.1.3 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/omnichannel-services@0.0.4 +- @rocket.chat/pdf-worker@0.0.4 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index d99b89d16e42..3503f8b5b6f7 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/omnichannel-transcript", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/presence-service/CHANGELOG.md b/ee/apps/presence-service/CHANGELOG.md index 8891187ff08f..30696edfe9fb 100644 --- a/ee/apps/presence-service/CHANGELOG.md +++ b/ee/apps/presence-service/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/presence-service +## 0.1.3 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/presence@0.0.4 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index 9cccd3703f3e..ec56bc6c49c2 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/presence-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Presence service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/queue-worker/CHANGELOG.md b/ee/apps/queue-worker/CHANGELOG.md index 08b199f14d9c..6a68c3564055 100644 --- a/ee/apps/queue-worker/CHANGELOG.md +++ b/ee/apps/queue-worker/CHANGELOG.md @@ -1,5 +1,15 @@ # @rocket.chat/queue-worker +## 0.1.3 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/omnichannel-services@0.0.4 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index efdfae6cd15b..7503f17157c0 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/queue-worker", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/apps/stream-hub-service/CHANGELOG.md b/ee/apps/stream-hub-service/CHANGELOG.md index de639f94831a..ca5e418b66a4 100644 --- a/ee/apps/stream-hub-service/CHANGELOG.md +++ b/ee/apps/stream-hub-service/CHANGELOG.md @@ -1,5 +1,14 @@ # @rocket.chat/stream-hub-service +## 0.1.3 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.1.2 ### Patch Changes diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index 3d481a9160b7..cc1a1686a940 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -1,7 +1,7 @@ { "name": "@rocket.chat/stream-hub-service", "private": true, - "version": "0.1.2", + "version": "0.1.3", "description": "Rocket.Chat Stream Hub service", "scripts": { "build": "tsc -p tsconfig.json", diff --git a/ee/packages/omnichannel-services/CHANGELOG.md b/ee/packages/omnichannel-services/CHANGELOG.md index 4b169eff955c..927c811f8eef 100644 --- a/ee/packages/omnichannel-services/CHANGELOG.md +++ b/ee/packages/omnichannel-services/CHANGELOG.md @@ -1,5 +1,16 @@ # @rocket.chat/omnichannel-services +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 +- @rocket.chat/pdf-worker@0.0.4 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/model-typings@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index 3eefbc9cd26a..8e88592b2d1e 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/omnichannel-services", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/ee/packages/pdf-worker/CHANGELOG.md b/ee/packages/pdf-worker/CHANGELOG.md index bf44622d6166..c45be6460384 100644 --- a/ee/packages/pdf-worker/CHANGELOG.md +++ b/ee/packages/pdf-worker/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/pdf-worker +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index fab3db3504c0..be365fb5cb05 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/pdf-worker", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@storybook/addon-actions": "~6.5.14", diff --git a/ee/packages/presence/CHANGELOG.md b/ee/packages/presence/CHANGELOG.md index bc882fd8ec0f..c7db8f4fbc25 100644 --- a/ee/packages/presence/CHANGELOG.md +++ b/ee/packages/presence/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/presence +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/core-services@0.0.4 +- @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 6894a268f110..9112b3daa68c 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/presence", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@babel/core": "^7.20.5", diff --git a/package.json b/package.json index 5e6cc689277d..f12f83ec5e71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket.chat", - "version": "6.2.8", + "version": "6.2.9", "description": "Rocket.Chat Monorepo", "main": "index.js", "private": true, diff --git a/packages/api-client/CHANGELOG.md b/packages/api-client/CHANGELOG.md index 6e3b931cfdab..1ed3f42198a7 100644 --- a/packages/api-client/CHANGELOG.md +++ b/packages/api-client/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/api-client +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/packages/api-client/package.json b/packages/api-client/package.json index 1fad6dbde4e0..168cc248689a 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/api-client", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/core-services/CHANGELOG.md b/packages/core-services/CHANGELOG.md index f9f550c86fb5..121a5c887b43 100644 --- a/packages/core-services/CHANGELOG.md +++ b/packages/core-services/CHANGELOG.md @@ -1,5 +1,13 @@ # @rocket.chat/core-services +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 +- @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/packages/core-services/package.json b/packages/core-services/package.json index a608d182e65d..44417b19e782 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-services", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/core-typings/CHANGELOG.md b/packages/core-typings/CHANGELOG.md index 11526ef6d5a9..217cf6e00d2d 100644 --- a/packages/core-typings/CHANGELOG.md +++ b/packages/core-typings/CHANGELOG.md @@ -1,5 +1,7 @@ # @rocket.chat/core-typings +## 6.2.9 + ## 6.2.7 ## 6.2.6 diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index ce13876bb9f7..6895e7ac55ca 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/core-typings", - "version": "6.2.8", + "version": "6.2.9", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/instance-status/CHANGELOG.md b/packages/instance-status/CHANGELOG.md index e8726035358d..b8c4c4709a30 100644 --- a/packages/instance-status/CHANGELOG.md +++ b/packages/instance-status/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/instance-status +## 0.0.4 + +### Patch Changes + +- @rocket.chat/models@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index 1a9cbeb3967b..7002956ed343 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/instance-status", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/model-typings/CHANGELOG.md b/packages/model-typings/CHANGELOG.md index 71417ee3e9ef..8aa35017d6d8 100644 --- a/packages/model-typings/CHANGELOG.md +++ b/packages/model-typings/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/model-typings +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 312410396b9c..690ffe0c21c1 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/model-typings", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index 87ccf5f8c057..5b69fbd9dbe8 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/models +## 0.0.4 + +### Patch Changes + +- @rocket.chat/model-typings@0.0.4 + ## 0.0.3 ### Patch Changes diff --git a/packages/models/package.json b/packages/models/package.json index 99909f53ec30..d9ed6794d8ba 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/models", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@types/jest": "~29.5.0", diff --git a/packages/release-action/src/bumpNextVersion.ts b/packages/release-action/src/bumpNextVersion.ts index cc977900b213..1184209062ef 100644 --- a/packages/release-action/src/bumpNextVersion.ts +++ b/packages/release-action/src/bumpNextVersion.ts @@ -78,7 +78,7 @@ export async function bumpNextVersion({ core.info('creating pull request'); await octokit.rest.pulls.create({ - base: 'master', + base: 'release-automation', head: newBranch, title: finalPrTitle, body: prBody, diff --git a/packages/release-action/src/startPatchRelease.ts b/packages/release-action/src/startPatchRelease.ts index 05681604e47c..a499b223608c 100644 --- a/packages/release-action/src/startPatchRelease.ts +++ b/packages/release-action/src/startPatchRelease.ts @@ -48,7 +48,7 @@ export async function startPatchRelease({ core.info('creating pull request'); await octokit.rest.pulls.create({ - base: 'master', + base: 'release-automation', head: newBranch, title: finalPrTitle, body: '', diff --git a/packages/rest-typings/CHANGELOG.md b/packages/rest-typings/CHANGELOG.md index 39debf6d6189..78f87727843d 100644 --- a/packages/rest-typings/CHANGELOG.md +++ b/packages/rest-typings/CHANGELOG.md @@ -1,5 +1,11 @@ # @rocket.chat/rest-typings +## 6.2.9 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 + ## 6.2.7 ### Patch Changes diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 63f5bca2464b..57bcd3112dc3 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/rest-typings", - "version": "6.2.8", + "version": "6.2.9", "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", diff --git a/packages/ui-contexts/CHANGELOG.md b/packages/ui-contexts/CHANGELOG.md index 465c0fad5029..f9ddf296fb60 100644 --- a/packages/ui-contexts/CHANGELOG.md +++ b/packages/ui-contexts/CHANGELOG.md @@ -1,5 +1,12 @@ # @rocket.chat/ui-contexts +## 0.0.4 + +### Patch Changes + +- @rocket.chat/core-typings@6.2.9 +- @rocket.chat/rest-typings@6.2.9 + ## 0.0.3 ### Patch Changes diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 123a092e2958..31793d37f3c0 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -1,6 +1,6 @@ { "name": "@rocket.chat/ui-contexts", - "version": "0.0.3", + "version": "0.0.4", "private": true, "devDependencies": { "@rocket.chat/core-typings": "workspace:^", From 6fe38a487b4547053240366c1678aab50ed5575d Mon Sep 17 00:00:00 2001 From: Ayush Sharma <89914602+ayush3160@users.noreply.github.com> Date: Wed, 5 Jul 2023 23:35:32 +0530 Subject: [PATCH 070/149] fix: Time format in quoted message according to user preference (#28912) Co-authored-by: Debdut Chakraborty <debdut.chakraborty@rocket.chat> Co-authored-by: Hugo Costa <hugocarreiracosta@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .changeset/timeFormat.md | 5 +++ .../content/attachments/QuoteAttachment.tsx | 4 +-- apps/meteor/client/hooks/useTimeAgo.ts | 33 +++++++++++++++---- 3 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 .changeset/timeFormat.md diff --git a/.changeset/timeFormat.md b/.changeset/timeFormat.md new file mode 100644 index 000000000000..447dc244fcbf --- /dev/null +++ b/.changeset/timeFormat.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed different time formats at different places diff --git a/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx b/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx index b7885563565a..67232cbd441f 100644 --- a/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/QuoteAttachment.tsx @@ -36,7 +36,7 @@ type QuoteAttachmentProps = { }; export const QuoteAttachment = ({ attachment }: QuoteAttachmentProps): ReactElement => { - const format = useTimeAgo(); + const formatTime = useTimeAgo(); return ( <> @@ -61,7 +61,7 @@ export const QuoteAttachment = ({ attachment }: QuoteAttachmentProps): ReactElem fontScale='c1' {...(attachment.message_link ? { is: 'a', href: attachment.message_link, color: 'hint' } : { color: 'hint' })} > - {format(attachment.ts)} + {formatTime(attachment.ts)} </Box> )} </AttachmentAuthor> diff --git a/apps/meteor/client/hooks/useTimeAgo.ts b/apps/meteor/client/hooks/useTimeAgo.ts index d2abb57017d1..81c5f732d042 100644 --- a/apps/meteor/client/hooks/useTimeAgo.ts +++ b/apps/meteor/client/hooks/useTimeAgo.ts @@ -1,22 +1,41 @@ -import { useTranslation } from '@rocket.chat/ui-contexts'; +import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; import moment from 'moment'; import { useCallback } from 'react'; -export const useTimeAgo = (): ((time: Date | number | string) => string) => - useCallback((time) => moment(time).calendar(null, { sameDay: 'LT', lastWeek: 'dddd LT', sameElse: 'LL' }), []); +import { t } from '../../app/utils/lib/i18n'; + +const dayFormat = ['h:mm A', 'H:mm'] as const; + +export const useTimeAgo = (): ((time: Date | number | string) => string) => { + const clockMode = useUserPreference<1 | 2>('clockMode'); + const timeFormat = useSetting('Message_TimeFormat') as string; + const format = clockMode !== undefined ? dayFormat[clockMode - 1] : timeFormat; + return useCallback( + (time) => + moment(time).calendar(null, { + sameDay: format, + lastDay: moment().localeData().calendar('lastDay').replace('LT', format), + lastWeek: `dddd ${format}`, + sameElse: 'LL', + }), + [format], + ); +}; export const useShortTimeAgo = (): ((time: Date | string | number) => string) => { - const t = useTranslation(); + const clockMode = useUserPreference<1 | 2>('clockMode'); + const timeFormat = useSetting('Message_TimeFormat') as string; + const format = clockMode !== undefined ? dayFormat[clockMode - 1] : timeFormat; return useCallback( (time) => moment(time).calendar(null, { - sameDay: 'LT', + sameDay: format, lastDay: `[${t('Yesterday')}]`, lastWeek: 'dddd', sameElse(now) { /* Using only this.isBefore(): - + ERRORS: Cannot invoke an object which is possibly 'undefined'. This expression is not callable. @@ -29,6 +48,6 @@ export const useShortTimeAgo = (): ((time: Date | string | number) => string) => return 'MMM Do'; }, }), - [], + [format], ); }; From 5e387a1b2e9855e8c8bc2096ac5700da8a7ea9d4 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Wed, 5 Jul 2023 23:38:28 +0530 Subject: [PATCH 071/149] fix: Toggle message box formatting toolbar on click (#29727) Co-authored-by: gabriellsh <40830821+gabriellsh@users.noreply.github.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .changeset/fuzzy-parents-drop.md | 5 +++++ .../FormattingToolbarDropdown.tsx | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/fuzzy-parents-drop.md diff --git a/.changeset/fuzzy-parents-drop.md b/.changeset/fuzzy-parents-drop.md new file mode 100644 index 000000000000..a0f8c56ea58f --- /dev/null +++ b/.changeset/fuzzy-parents-drop.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Fix Toggle message box formatting toolbar on click diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx index e44c2521616a..bdee3ce6d7d1 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxFormattingToolbar/FormattingToolbarDropdown.tsx @@ -34,7 +34,13 @@ const FormattingToolbarDropdown = ({ composer, items, ...props }: FormattingTool }; return ( - <Option key={index} onClick={handleFormattingAction}> + <Option + key={index} + onClick={() => { + handleFormattingAction(); + toggle(); + }} + > <OptionIcon name={'icon' in formatter ? formatter.icon : 'link'} /> <OptionContent>{t(formatter.label)}</OptionContent> </Option> From 4da8801830b8b1a9d13b169358c2aff5843230ba Mon Sep 17 00:00:00 2001 From: Tasso Evangelista <tasso.evangelista@rocket.chat> Date: Wed, 5 Jul 2023 15:21:13 -0300 Subject: [PATCH 072/149] chore: Use FlowRoute through a facade (#29507) --- apps/meteor/app/analytics/client/index.ts | 1 - .../meteor/app/analytics/client/loadScript.ts | 16 +- .../app/analytics/client/trackEvents.js | 244 ---------- .../server/functions/sendMail.ts | 6 +- .../client/actionButton.ts | 4 +- .../app/slashcommands-open/client/client.ts | 6 +- .../app/threads/client/flextab/threadlist.tsx | 2 +- .../client/messageAction/replyInThread.ts | 12 +- .../app/ui-message/client/ActionManager.js | 11 +- .../app/ui-utils/client/lib/AccountBox.ts | 4 +- .../ui-utils/client/lib/LegacyRoomManager.ts | 22 +- .../client/lib/messageActionDefault.ts | 4 +- .../app/ui-utils/client/lib/readMessages.ts | 2 +- .../app/ui/client/lib/KonchatNotification.ts | 32 +- .../client/components/GazzodownText.tsx | 14 +- .../client/components/NotFoundState.tsx | 8 +- .../Sidebar/SidebarItemsAssembler.tsx | 1 - .../Sidebar/SidebarNavigationItem.tsx | 9 +- .../client/hooks/useAnalyticsEventTracking.ts | 258 ++++++++++ apps/meteor/client/hooks/useDefaultRoute.ts | 22 - apps/meteor/client/hooks/useEmbeddedLayout.ts | 4 +- apps/meteor/client/importPackages.ts | 1 - apps/meteor/client/lib/appLayout.tsx | 16 +- .../client/lib/chats/flows/replyBroadcast.ts | 6 +- apps/meteor/client/lib/createRouteGroup.tsx | 196 +++----- apps/meteor/client/lib/createSidebarItems.ts | 7 +- .../lib/errors/VisitorDoesNotExistError.ts | 7 + .../client/lib/rooms/roomCoordinator.tsx | 48 +- .../client/lib/rooms/roomTypes/direct.ts | 2 +- .../client/lib/rooms/roomTypes/livechat.ts | 2 +- .../client/lib/rooms/roomTypes/private.ts | 2 +- .../client/lib/rooms/roomTypes/public.ts | 2 +- .../meteor/client/lib/rooms/roomTypes/voip.ts | 2 +- apps/meteor/client/lib/tracker.ts | 8 +- apps/meteor/client/lib/userData.ts | 4 +- apps/meteor/client/lib/utils/goToRoomById.ts | 6 +- .../client/lib/utils/isLayoutEmbedded.ts | 4 +- .../client/lib/utils/legacyJumpToMessage.ts | 29 +- .../setMessageJumpQueryStringParameter.ts | 13 +- .../providers/CallProvider/CallProvider.tsx | 9 +- .../client/providers/LayoutProvider.tsx | 10 +- .../client/providers/RouterProvider.tsx | 252 +++++++--- apps/meteor/client/sidebar/Item/Medium.tsx | 4 +- apps/meteor/client/sidebar/RoomMenu.tsx | 8 +- .../sidebar/header/actions/Directory.tsx | 12 +- .../client/sidebar/header/actions/Home.tsx | 6 +- .../actions/hooks/useAdministrationItems.tsx | 6 +- apps/meteor/client/startup/e2e.ts | 4 +- apps/meteor/client/startup/iframeCommands.ts | 15 +- apps/meteor/client/startup/loginViaQuery.ts | 23 +- .../notifications/konchatNotifications.ts | 6 +- .../client/startup/reloadRoomAfterLogin.ts | 7 +- apps/meteor/client/startup/routes.tsx | 448 ++++++++---------- apps/meteor/client/startup/setupWizard.ts | 4 +- .../stories/contexts/RouterContextMock.tsx | 4 +- .../client/views/account/AccountRouter.tsx | 21 +- .../client/views/account/AccountSidebar.tsx | 6 +- apps/meteor/client/views/account/routes.tsx | 33 ++ .../client/views/account/sidebarItems.ts | 12 +- .../views/admin/AdministrationRouter.tsx | 58 ++- .../views/admin/import/ImportHistoryPage.tsx | 11 +- .../admin/import/ImportOperationSummary.js | 9 +- .../views/admin/import/ImportProgressPage.tsx | 21 +- .../views/admin/import/NewImportPage.js | 26 +- .../views/admin/import/PrepareImportPage.js | 46 +- .../FederationModal/InviteUsers.tsx | 7 +- .../admin/moderation/MessageReportInfo.tsx | 3 +- apps/meteor/client/views/admin/routes.tsx | 98 ++++ .../admin/settings/SettingsGroupCard.tsx | 26 +- .../views/admin/sidebar/AdminSidebar.tsx | 6 +- .../client/views/admin/sidebar/UpgradeTab.tsx | 24 +- .../meteor/client/views/admin/sidebarItems.ts | 36 +- .../admin/upgrade/UpgradePage/UpgradePage.tsx | 4 +- .../views/conference/ConferencePage.tsx | 2 +- .../client/views/directory/DirectoryPage.tsx | 35 +- apps/meteor/client/views/home/HomePage.tsx | 34 +- .../client/views/home/cards/JoinRoomsCard.tsx | 6 +- .../meteor/client/views/invite/InvitePage.tsx | 6 +- .../client/views/invite/SecretURLPage.tsx | 8 +- .../AppDetailsPage/AppDetailsPage.tsx | 25 +- .../AppDetailsPage/AppDetailsPageTabs.tsx | 31 +- .../views/marketplace/AppInstallPage.js | 41 +- .../client/views/marketplace/AppMenu.js | 32 +- .../views/marketplace/AppsList/AppRow.tsx | 26 +- .../views/marketplace/AppsPage/AppsPage.tsx | 10 +- .../marketplace/AppsPage/AppsPageContent.tsx | 18 +- .../views/marketplace/MarketplaceSidebar.tsx | 8 +- .../client/views/marketplace/routes.tsx | 13 + .../client/views/marketplace/sidebarItems.tsx | 10 +- apps/meteor/client/views/meet/MeetPage.tsx | 16 +- apps/meteor/client/views/meet/MeetRoute.tsx | 63 +++ .../views/oauth/OAuthAuthorizationPage.tsx | 6 +- .../components/AuthorizationFormPage.tsx | 2 +- .../views/omnichannel/OmnichannelRouter.tsx | 29 +- .../directory/CallsContextualBarDirectory.tsx | 4 +- .../directory/OmnichannelDirectoryPage.tsx | 38 +- .../contacts/contextualBar/ContactInfo.tsx | 49 +- .../meteor/client/views/omnichannel/routes.ts | 73 +++ .../sidebar/OmnichannelSidebar.tsx | 8 +- .../client/views/omnichannel/sidebarItems.ts | 24 +- .../room/Header/Omnichannel/BackButton.tsx | 16 +- .../Omnichannel/OmnichannelRoomHeader.tsx | 20 +- .../QuickActions/hooks/useQuickActions.tsx | 10 +- .../Header/Omnichannel/VoipRoomHeader.tsx | 19 +- .../MessageList/hooks/useJumpToMessage.ts | 16 +- .../room/MessageList/hooks/useMessages.ts | 2 +- .../providers/MessageListProvider.tsx | 4 +- apps/meteor/client/views/room/RoomRoute.tsx | 29 ++ .../views/room/components/body/RoomBody.tsx | 31 +- .../Info/EditRoomInfo/EditChannel.js | 6 +- .../Info/hooks/actions/useRoomDelete.tsx | 6 +- .../Info/hooks/actions/useRoomHide.tsx | 6 +- .../Info/hooks/actions/useRoomLeave.tsx | 6 +- .../Threads/hooks/useGetMessageByID.ts | 2 +- .../hooks/useLegacyThreadMessageJump.ts | 30 +- .../views/room/hooks/useAppsContextualBar.ts | 5 +- .../client/views/room/hooks/useGoToRoom.ts | 10 +- .../client/views/room/hooks/useGoToThread.ts | 26 +- .../views/room/hooks/useGoToThreadList.ts | 27 +- .../client/views/room/hooks/useOpenRoom.ts | 4 +- .../actions/useRedirectModerationConsole.ts | 2 +- .../views/room/providers/RoomProvider.tsx | 10 +- .../views/room/providers/ToolboxProvider.tsx | 47 +- apps/meteor/client/views/root/AppLayout.tsx | 13 +- apps/meteor/client/views/root/IndexRoute.tsx | 42 ++ apps/meteor/client/views/root/LoginRoute.tsx | 14 + .../client/views/root/LoginTokenRoute.tsx | 25 + .../root/MainLayout/LayoutWithSidebar.tsx | 5 +- .../views/setupWizard/hooks/useRouteLock.ts | 8 +- .../contextualBar/info/TeamsInfoWithData.js | 10 +- apps/meteor/definition/IRoomTypeConfig.ts | 11 +- .../definition/externals/meteor/http.d.ts | 53 +++ .../externals/meteor/kadira-flow-router.d.ts | 82 ++-- .../definition/externals/meteor/meteor.d.ts | 2 + .../client/views/livechatSideNavItems.js | 12 +- .../contextualBar/CannedResponse/index.tsx | 16 +- apps/meteor/ee/client/omnichannel/routes.ts | 21 + apps/meteor/ee/client/startup/audit.tsx | 94 ++-- .../ee/client/startup/deviceManagement.ts | 15 +- .../ee/client/startup/engagementDashboard.ts | 9 + .../EngagementDashboardRoute.tsx | 47 +- .../client/views/audit/hooks/useAuditTab.ts | 2 +- apps/meteor/lib/callbacks.ts | 14 +- apps/meteor/lib/rooms/roomTypes/direct.ts | 9 + apps/meteor/lib/rooms/roomTypes/livechat.ts | 9 + apps/meteor/lib/rooms/roomTypes/private.ts | 9 + apps/meteor/lib/rooms/roomTypes/public.ts | 9 + apps/meteor/lib/rooms/roomTypes/voip.ts | 9 + apps/meteor/lib/utils/generatePath.ts | 13 + apps/meteor/package.json | 2 +- .../omnichannel-contact-center.spec.ts | 2 +- .../omnichannel-departaments.spec.ts | 26 +- .../fragments/omnichannel-sidenav.ts | 12 +- .../page-objects/omnichannel-departments.ts | 5 + .../tests/mocks/client/RouterContextMock.tsx | 147 ++++-- .../views/notFound/NotFoundPage.spec.tsx | 11 +- apps/meteor/tsconfig.json | 2 +- packages/ui-contexts/.eslintrc.json | 2 +- packages/ui-contexts/package.json | 1 + packages/ui-contexts/src/RouterContext.ts | 128 +++-- .../ui-contexts/src/hooks/useCurrentRoute.ts | 12 - .../src/hooks/useCurrentRoutePath.ts | 21 + packages/ui-contexts/src/hooks/useLogout.ts | 6 +- .../src/hooks/useQueryStringParameter.ts | 12 - packages/ui-contexts/src/hooks/useRoute.ts | 25 +- .../src/hooks/useRouteParameter.ts | 10 +- .../ui-contexts/src/hooks/useRoutePath.ts | 20 - packages/ui-contexts/src/hooks/useRouteUrl.ts | 20 - packages/ui-contexts/src/hooks/useRouter.ts | 5 + .../src/hooks/useSearchParameter.ts | 15 + .../src/hooks/useSearchParameters.ts | 9 + packages/ui-contexts/src/index.ts | 10 +- .../src/ResetPassword/ResetPasswordPage.tsx | 6 +- yarn.lock | 1 + 174 files changed, 2602 insertions(+), 1714 deletions(-) delete mode 100644 apps/meteor/app/analytics/client/index.ts delete mode 100644 apps/meteor/app/analytics/client/trackEvents.js create mode 100644 apps/meteor/client/hooks/useAnalyticsEventTracking.ts delete mode 100644 apps/meteor/client/hooks/useDefaultRoute.ts create mode 100644 apps/meteor/client/lib/errors/VisitorDoesNotExistError.ts create mode 100644 apps/meteor/client/views/meet/MeetRoute.tsx create mode 100644 apps/meteor/client/views/room/RoomRoute.tsx create mode 100644 apps/meteor/client/views/root/IndexRoute.tsx create mode 100644 apps/meteor/client/views/root/LoginRoute.tsx create mode 100644 apps/meteor/client/views/root/LoginTokenRoute.tsx create mode 100644 apps/meteor/definition/externals/meteor/http.d.ts create mode 100644 apps/meteor/lib/utils/generatePath.ts delete mode 100644 packages/ui-contexts/src/hooks/useCurrentRoute.ts create mode 100644 packages/ui-contexts/src/hooks/useCurrentRoutePath.ts delete mode 100644 packages/ui-contexts/src/hooks/useQueryStringParameter.ts delete mode 100644 packages/ui-contexts/src/hooks/useRoutePath.ts delete mode 100644 packages/ui-contexts/src/hooks/useRouteUrl.ts create mode 100644 packages/ui-contexts/src/hooks/useRouter.ts create mode 100644 packages/ui-contexts/src/hooks/useSearchParameter.ts create mode 100644 packages/ui-contexts/src/hooks/useSearchParameters.ts diff --git a/apps/meteor/app/analytics/client/index.ts b/apps/meteor/app/analytics/client/index.ts deleted file mode 100644 index dbce9bb4aa21..000000000000 --- a/apps/meteor/app/analytics/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './trackEvents'; diff --git a/apps/meteor/app/analytics/client/loadScript.ts b/apps/meteor/app/analytics/client/loadScript.ts index af90cd8ea292..47d4a10f764a 100644 --- a/apps/meteor/app/analytics/client/loadScript.ts +++ b/apps/meteor/app/analytics/client/loadScript.ts @@ -7,15 +7,15 @@ import { useReactiveValue } from '../../../client/hooks/useReactiveValue'; declare global { // eslint-disable-next-line @typescript-eslint/naming-convention interface Window { - _paq: [string, ...unknown[]][]; + _paq?: [string, ...unknown[]][]; GoogleAnalyticsObject: unknown; - ga: qa; + ga?: qa; } type qa = { (...args: unknown[]): void; - l: number; - q: unknown[]; + l?: number; + q?: unknown[]; }; } @@ -60,17 +60,17 @@ export const useAnalytics = (): void => { (i[r] = i[r] || function () { - (i[r].q = i[r].q || []).push(arguments); + ((i[r] as any).q = (i[r] as any).q || []).push(arguments); }), - (i[r].l = new Date().getTime()); + ((i[r] as any).l = new Date().getTime()); (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]); a.async = 1; a.src = g; m.parentNode.insertBefore(a, m); })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); - window.ga('create', googleId, 'auto'); - window.ga('send', 'pageview'); + window.ga?.('create', googleId, 'auto'); + window.ga?.('send', 'pageview'); } /* eslint-enable */ }, [googleId, uid]); diff --git a/apps/meteor/app/analytics/client/trackEvents.js b/apps/meteor/app/analytics/client/trackEvents.js deleted file mode 100644 index 1d274f03aac4..000000000000 --- a/apps/meteor/app/analytics/client/trackEvents.js +++ /dev/null @@ -1,244 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Tracker } from 'meteor/tracker'; - -import { settings } from '../../settings/client'; -import { callbacks } from '../../../lib/callbacks'; -import { ChatRoom } from '../../models/client'; - -function trackEvent(category, action, label) { - if (window._paq) { - window._paq.push(['trackEvent', category, action, label]); - } - if (window.ga) { - window.ga('send', 'event', category, action, label); - } -} - -if (!window._paq || window.ga) { - // Trigger the trackPageView manually as the page views are only loaded when the loadScript.js code is executed - FlowRouter.triggers.enter([ - (route) => { - if (window._paq) { - const http = location.protocol; - const slashes = http.concat('//'); - const host = slashes.concat(window.location.hostname); - window._paq.push(['setCustomUrl', host + route.path]); - window._paq.push(['trackPageView']); - } - if (window.ga) { - window.ga('send', 'pageview', route.path); - } - }, - ]); - - // Login page has manual switches - callbacks.add( - 'loginPageStateChange', - (state) => { - trackEvent('Navigation', 'Login Page State Change', state); - }, - callbacks.priority.MEDIUM, - 'analytics-login-state-change', - ); - - // Messsages - callbacks.add( - 'afterSaveMessage', - (message) => { - if ((window._paq || window.ga) && settings.get('Analytics_features_messages')) { - const room = ChatRoom.findOne({ _id: message.rid }); - trackEvent('Message', 'Send', `${room.name} (${room._id})`); - } - }, - 2000, - 'trackEvents', - ); - - // Rooms - callbacks.add( - 'afterCreateChannel', - (owner, room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Create', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-after-create-channel', - ); - - callbacks.add( - 'roomNameChanged', - (room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Changed Name', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-room-name-changed', - ); - - callbacks.add( - 'roomTopicChanged', - (room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Changed Topic', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-room-topic-changed', - ); - - callbacks.add( - 'roomAnnouncementChanged', - (room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Changed Announcement', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-room-announcement-changed', - ); - - callbacks.add( - 'roomTypeChanged', - (room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Changed Room Type', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-room-type-changed', - ); - - callbacks.add( - 'archiveRoom', - (room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Archived', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-archive-room', - ); - - callbacks.add( - 'unarchiveRoom', - (room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Unarchived', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-unarchive-room', - ); - - // Users - // Track logins and associate user ids with piwik - (() => { - let oldUserId = null; - - Tracker.autorun(() => { - const newUserId = Meteor.userId(); - if (oldUserId === null && newUserId) { - if (window._paq && settings.get('Analytics_features_users')) { - trackEvent('User', 'Login', newUserId); - window._paq.push(['setUserId', newUserId]); - } - } else if (newUserId === null && oldUserId) { - if (window._paq && settings.get('Analytics_features_users')) { - trackEvent('User', 'Logout', oldUserId); - } - } - oldUserId = Meteor.userId(); - }); - })(); - - callbacks.add( - 'userRegistered', - () => { - if (settings.get('Analytics_features_users')) { - trackEvent('User', 'Registered'); - } - }, - callbacks.priority.MEDIUM, - 'piwik-user-resitered', - ); - - callbacks.add( - 'usernameSet', - () => { - if (settings.get('Analytics_features_users')) { - trackEvent('User', 'Username Set'); - } - }, - callbacks.priority.MEDIUM, - 'piweik-username-set', - ); - - callbacks.add( - 'userPasswordReset', - () => { - if (settings.get('Analytics_features_users')) { - trackEvent('User', 'Reset Password'); - } - }, - callbacks.priority.MEDIUM, - 'piwik-user-password-reset', - ); - - callbacks.add( - 'userConfirmationEmailRequested', - () => { - if (settings.get('Analytics_features_users')) { - trackEvent('User', 'Confirmation Email Requested'); - } - }, - callbacks.priority.MEDIUM, - 'piwik-user-confirmation-email-requested', - ); - - callbacks.add( - 'userForgotPasswordEmailRequested', - () => { - if (settings.get('Analytics_features_users')) { - trackEvent('User', 'Forgot Password Email Requested'); - } - }, - callbacks.priority.MEDIUM, - 'piwik-user-forgot-password-email-requested', - ); - - callbacks.add( - 'userStatusManuallySet', - (status) => { - if (settings.get('Analytics_features_users')) { - trackEvent('User', 'Status Manually Changed', status); - } - }, - callbacks.priority.MEDIUM, - 'analytics-user-status-manually-set', - ); - - callbacks.add( - 'userAvatarSet', - (service) => { - if (settings.get('Analytics_features_users')) { - trackEvent('User', 'Avatar Changed', service); - } - }, - callbacks.priority.MEDIUM, - 'analytics-user-avatar-set', - ); - - callbacks.add( - 'roomAvatarChanged', - (room) => { - if (settings.get('Analytics_features_rooms')) { - trackEvent('Room', 'Changed Avatar', `${room.name} (${room._id})`); - } - }, - callbacks.priority.MEDIUM, - 'analytics-room-avatar-changed', - ); -} diff --git a/apps/meteor/app/mail-messages/server/functions/sendMail.ts b/apps/meteor/app/mail-messages/server/functions/sendMail.ts index 47acb6da1d9d..881b5eb7f706 100644 --- a/apps/meteor/app/mail-messages/server/functions/sendMail.ts +++ b/apps/meteor/app/mail-messages/server/functions/sendMail.ts @@ -1,6 +1,5 @@ import { Meteor } from 'meteor/meteor'; import EJSON from 'ejson'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { escapeHTML } from '@rocket.chat/string-helpers'; import type { Filter } from 'mongodb'; import type { IUser } from '@rocket.chat/core-typings'; @@ -9,6 +8,7 @@ import { Users } from '@rocket.chat/models'; import { placeholders } from '../../../utils/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; import * as Mailer from '../../../mailer/server/api'; +import { generatePath } from '../../../../lib/utils/generatePath'; export const sendMail = async function ({ from, @@ -44,7 +44,7 @@ export const sendMail = async function ({ const email = `${user.name} <${user.emails?.[0].address}>`; const html = placeholders.replace(body, { unsubscribe: Meteor.absoluteUrl( - FlowRouter.path('mailer/unsubscribe/:_id/:createdAt', { + generatePath('mailer/unsubscribe/:_id/:createdAt', { _id: user._id, createdAt: user.createdAt?.getTime().toString() || '', }), @@ -70,7 +70,7 @@ export const sendMail = async function ({ const html = placeholders.replace(body, { unsubscribe: Meteor.absoluteUrl( - FlowRouter.path('mailer/unsubscribe/:_id/:createdAt', { + generatePath('mailer/unsubscribe/:_id/:createdAt', { _id: user._id, createdAt: user.createdAt?.getTime().toString() || '', }), diff --git a/apps/meteor/app/message-mark-as-unread/client/actionButton.ts b/apps/meteor/app/message-mark-as-unread/client/actionButton.ts index 60d78c888549..070d1ea750a4 100644 --- a/apps/meteor/app/message-mark-as-unread/client/actionButton.ts +++ b/apps/meteor/app/message-mark-as-unread/client/actionButton.ts @@ -1,5 +1,4 @@ import { Meteor } from 'meteor/meteor'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { LegacyRoomManager, MessageAction } from '../../ui-utils/client'; import { messageArgs } from '../../../client/lib/utils/messageArgs'; @@ -7,6 +6,7 @@ import { ChatSubscription } from '../../models/client'; import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; import { dispatchToastMessage } from '../../../client/lib/toast'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { router } from '../../../client/providers/RouterProvider'; Meteor.startup(() => { MessageAction.addButton({ @@ -27,7 +27,7 @@ Meteor.startup(() => { return; } await LegacyRoomManager.close(subscription.t + subscription.name); - return FlowRouter.go('home'); + return router.navigate('/home'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } diff --git a/apps/meteor/app/slashcommands-open/client/client.ts b/apps/meteor/app/slashcommands-open/client/client.ts index 3bf88010b721..696806619965 100644 --- a/apps/meteor/app/slashcommands-open/client/client.ts +++ b/apps/meteor/app/slashcommands-open/client/client.ts @@ -1,11 +1,11 @@ import type { RoomType, ISubscription, SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import type { Mongo } from 'meteor/mongo'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator'; import { slashCommands } from '../../utils/lib/slashCommand'; import { Subscriptions, ChatSubscription } from '../../models/client'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { router } from '../../../client/providers/RouterProvider'; slashCommands.add({ command: 'open', @@ -26,7 +26,7 @@ slashCommands.add({ const subscription = ChatSubscription.findOne(query); if (subscription) { - roomCoordinator.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); + roomCoordinator.openRouteLink(subscription.t, subscription, router.getSearchParameters()); } if (type && type.indexOf('d') === -1) { @@ -38,7 +38,7 @@ slashCommands.add({ if (!subscription) { return; } - roomCoordinator.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); + roomCoordinator.openRouteLink(subscription.t, subscription, router.getSearchParameters()); } catch (err: unknown) { // noop } diff --git a/apps/meteor/app/threads/client/flextab/threadlist.tsx b/apps/meteor/app/threads/client/flextab/threadlist.tsx index 82fb993379f0..b1ca3b7b7a61 100644 --- a/apps/meteor/app/threads/client/flextab/threadlist.tsx +++ b/apps/meteor/app/threads/client/flextab/threadlist.tsx @@ -45,7 +45,7 @@ addAction('thread', (options) => { const unread = tunread > 99 ? '99+' : tunread; const variant = getVariant(tunreadUser, tunreadGroup); return ( - <HeaderToolboxAction {...props}> + <HeaderToolboxAction key={props.id} {...props}> {!!unread && <HeaderToolboxActionBadge variant={variant}>{unread}</HeaderToolboxActionBadge>} </HeaderToolboxAction> ); diff --git a/apps/meteor/app/threads/client/messageAction/replyInThread.ts b/apps/meteor/app/threads/client/messageAction/replyInThread.ts index 82b166310c27..632fbc426e4f 100644 --- a/apps/meteor/app/threads/client/messageAction/replyInThread.ts +++ b/apps/meteor/app/threads/client/messageAction/replyInThread.ts @@ -1,11 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { settings } from '../../../settings/client'; import { MessageAction } from '../../../ui-utils/client'; import { messageArgs } from '../../../../client/lib/utils/messageArgs'; import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; +import { router } from '../../../../client/providers/RouterProvider'; Meteor.startup(function () { Tracker.autorun(() => { @@ -20,9 +20,13 @@ Meteor.startup(function () { action(e, props) { const { message = messageArgs(this).msg } = props; e.stopPropagation(); - FlowRouter.setParams({ - tab: 'thread', - context: message.tmid || message._id, + router.navigate({ + name: router.getRouteName()!, + params: { + ...router.getRouteParameters(), + tab: 'thread', + context: message.tmid || message._id, + }, }); }, condition({ subscription, room }) { diff --git a/apps/meteor/app/ui-message/client/ActionManager.js b/apps/meteor/app/ui-message/client/ActionManager.js index 6e2aadd1babe..48165b99f436 100644 --- a/apps/meteor/app/ui-message/client/ActionManager.js +++ b/apps/meteor/app/ui-message/client/ActionManager.js @@ -1,6 +1,5 @@ import { UIKitIncomingInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; import { Meteor } from 'meteor/meteor'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Random } from '@rocket.chat/random'; import { Emitter } from '@rocket.chat/emitter'; import { UIKitInteractionTypes } from '@rocket.chat/core-typings'; @@ -13,6 +12,7 @@ import { dispatchToastMessage } from '../../../client/lib/toast'; import { imperativeModal } from '../../../client/lib/imperativeModal'; import UiKitModal from '../../../client/views/modal/uikit/UiKitModal'; import { sdk } from '../../utils/client/lib/SDKClient'; +import { router } from '../../../client/providers/RouterProvider'; const events = new Emitter(); @@ -124,7 +124,14 @@ const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) }, }); - FlowRouter.setParams({ tab: 'app', context: viewId }); + router.navigate({ + name: router.getRouteName(), + params: { + ...router.getRouteParameters(), + tab: 'app', + context: viewId, + }, + }); return UIKitInteractionTypes.CONTEXTUAL_BAR_OPEN; } diff --git a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts index b337ae4e98e5..3ed3a60b3bf6 100644 --- a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts @@ -2,7 +2,7 @@ import type { IUIActionButton, IUActionButtonWhen } from '@rocket.chat/apps-engi import type { UserStatus } from '@rocket.chat/core-typings'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; -import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import type { TranslationKey, LocationPathname } from '@rocket.chat/ui-contexts'; import type { Icon } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; @@ -22,7 +22,7 @@ export interface IAppAccountBoxItem extends IUIActionButton { export type AccountBoxItem = { name: TranslationKey; icon: ComponentProps<typeof Icon>['name']; - href: string; + href: LocationPathname; sideNav?: string; condition: () => boolean; }; diff --git a/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts b/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts index fb110c7b1e4f..f85fecd74ac1 100644 --- a/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts +++ b/apps/meteor/app/ui-utils/client/lib/LegacyRoomManager.ts @@ -1,7 +1,7 @@ import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import type { IMessage, IRoom } from '@rocket.chat/core-typings'; +import type { Mongo } from 'meteor/mongo'; import { fireGlobalEvent } from '../../../../client/lib/utils/fireGlobalEvent'; import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager'; @@ -13,6 +13,7 @@ import { RoomManager } from '../../../../client/lib/RoomManager'; import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; import { Notifications } from '../../../notifications/client'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { router } from '../../../../client/providers/RouterProvider'; const maxRoomsOpen = parseInt(getConfig('maxRoomsOpen') ?? '5') || 5; @@ -87,15 +88,18 @@ const handleTrackSettingsChange = (msg: IMessage) => { void Tracker.nonreactive(async () => { if (msg.t === 'room_changed_privacy') { - const type = FlowRouter.current().route?.name === 'channel' ? 'c' : 'p'; - await close(type + FlowRouter.getParam('name')); + const type = router.getRouteName() === 'channel' ? 'c' : 'p'; + await close(type + router.getRouteParameters().name); const subscription = ChatSubscription.findOne({ rid: msg.rid }); if (!subscription) { throw new Error('Subscription not found'); } - const route = subscription.t === 'c' ? 'channel' : 'group'; - FlowRouter.go(route, { name: subscription.name }, FlowRouter.current().queryParams); + router.navigate({ + pattern: subscription.t === 'c' ? '/channel/:name/:tab?/:context?' : '/group/:name/:tab?/:context?', + params: { name: subscription.name }, + search: router.getSearchParameters(), + }); } if (msg.t === 'r') { @@ -103,9 +107,9 @@ const handleTrackSettingsChange = (msg: IMessage) => { if (!room) { throw new Error('Room not found'); } - if (room.name !== FlowRouter.getParam('name')) { - await close(room.t + FlowRouter.getParam('name')); - roomCoordinator.openRouteLink(room.t, room, FlowRouter.current().queryParams); + if (room.name !== router.getRouteParameters().name) { + await close(room.t + router.getRouteParameters().name); + roomCoordinator.openRouteLink(room.t, room, router.getSearchParameters()); } } }); @@ -174,7 +178,7 @@ const computation = Tracker.autorun(() => { ChatMessage.update({ tmid: msg._id }, { $unset: { tmid: 1 } }, { multi: true }); }); Notifications.onRoom(record.rid, 'deleteMessageBulk', ({ rid, ts, excludePinned, ignoreDiscussion, users }) => { - const query: Mongo.Query<IMessage> = { rid, ts }; + const query: Mongo.Selector<IMessage> = { rid, ts }; if (excludePinned) { query.pinned = { $ne: true }; } diff --git a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts index 18ae57c68df4..6d3f856e2b9c 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts @@ -1,4 +1,3 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; import moment from 'moment'; import { Meteor } from 'meteor/meteor'; import type { IMessage } from '@rocket.chat/core-typings'; @@ -15,6 +14,7 @@ import ReactionList from '../../../../client/views/room/modals/ReactionListModal import ReportMessageModal from '../../../../client/views/room/modals/ReportMessageModal'; import { dispatchToastMessage } from '../../../../client/lib/toast'; import { t } from '../../../utils/lib/i18n'; +import { router } from '../../../../client/providers/RouterProvider'; const getMainMessageText = (message: IMessage): IMessage => { const newMessage = { ...message }; @@ -36,7 +36,7 @@ Meteor.startup(async function () { 'd', { name: message.u.username }, { - ...FlowRouter.current().queryParams, + ...router.getSearchParameters(), reply: message._id, }, ); diff --git a/apps/meteor/app/ui-utils/client/lib/readMessages.ts b/apps/meteor/app/ui-utils/client/lib/readMessages.ts index 7d1cbc86af31..6a09a503552f 100644 --- a/apps/meteor/app/ui-utils/client/lib/readMessages.ts +++ b/apps/meteor/app/ui-utils/client/lib/readMessages.ts @@ -150,7 +150,7 @@ class ReadMessage extends Emitter { $gt: lastReadRecord.ts, }, 'u._id': { - $ne: Meteor.userId(), + $ne: Meteor.userId() ?? undefined, }, }, { diff --git a/apps/meteor/app/ui/client/lib/KonchatNotification.ts b/apps/meteor/app/ui/client/lib/KonchatNotification.ts index 4561b198f040..38e9a807fd8c 100644 --- a/apps/meteor/app/ui/client/lib/KonchatNotification.ts +++ b/apps/meteor/app/ui/client/lib/KonchatNotification.ts @@ -1,6 +1,5 @@ import type { IMessage, IRoom, IUser, RoomType } from '@rocket.chat/core-typings'; import { ReactiveVar } from 'meteor/reactive-var'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Random } from '@rocket.chat/random'; import { Meteor } from 'meteor/meteor'; @@ -14,6 +13,7 @@ import { getAvatarAsPng } from '../../../../client/lib/utils/getAvatarAsPng'; import { stripTags } from '../../../../lib/utils/stringUtils'; import { RoomManager } from '../../../../client/lib/RoomManager'; import { sdk } from '../../../utils/client/lib/SDKClient'; +import { router } from '../../../../client/providers/RouterProvider'; declare global { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -111,41 +111,41 @@ class KonchatNotification { switch (notification.payload?.type) { case 'd': - return FlowRouter.go( - 'direct', - { + return router.navigate({ + pattern: '/direct/:rid/:tab?/:context?', + params: { rid: notification.payload.rid, ...(notification.payload.tmid && { tab: 'thread', context: notification.payload.tmid, }), }, - { ...FlowRouter.current().queryParams, jump: notification.payload._id }, - ); + search: { ...router.getSearchParameters(), jump: notification.payload._id }, + }); case 'c': - return FlowRouter.go( - 'channel', - { + return router.navigate({ + pattern: '/channel/:name/:tab?/:context?', + params: { name: notification.payload.name, ...(notification.payload.tmid && { tab: 'thread', context: notification.payload.tmid, }), }, - { ...FlowRouter.current().queryParams, jump: notification.payload._id }, - ); + search: { ...router.getSearchParameters(), jump: notification.payload._id }, + }); case 'p': - return FlowRouter.go( - 'group', - { + return router.navigate({ + pattern: '/group/:name/:tab?/:context?', + params: { name: notification.payload.name, ...(notification.payload.tmid && { tab: 'thread', context: notification.payload.tmid, }), }, - { ...FlowRouter.current().queryParams, jump: notification.payload._id }, - ); + search: { ...router.getSearchParameters(), jump: notification.payload._id }, + }); } }; } diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index 96a8981c193d..e9a0f39ddb5f 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -2,9 +2,9 @@ import type { IRoom } from '@rocket.chat/core-typings'; import type { ChannelMention, UserMention } from '@rocket.chat/gazzodown'; import { MarkupInteractionContext } from '@rocket.chat/gazzodown'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { RouterContext, useLayout, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useLayout, useRouter, useUserPreference } from '@rocket.chat/ui-contexts'; import type { UIEvent } from 'react'; -import React, { useContext, useCallback, memo, useMemo } from 'react'; +import React, { useCallback, memo, useMemo } from 'react'; import { detectEmoji } from '../lib/utils/detectEmoji'; import { fireGlobalEvent } from '../lib/utils/fireGlobalEvent'; @@ -79,18 +79,22 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe ); const goToRoom = useGoToRoom(); - const router = useContext(RouterContext); const { isEmbedded } = useLayout(); const resolveChannelMention = useCallback((mention: string) => channels?.find(({ name }) => name === mention), [channels]); + const router = useRouter(); + const onChannelMentionClick = useCallback( ({ _id: rid }: ChannelMention) => (event: UIEvent): void => { if (isEmbedded) { fireGlobalEvent('click-mention-link', { - path: router.getRoutePath('channel', { name: rid }), + path: router.buildRoutePath({ + pattern: '/channel/:name/:tab?/:context?', + params: { name: rid }, + }), channel: rid, }); } @@ -98,7 +102,7 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe event.stopPropagation(); goToRoom(rid); }, - [isEmbedded, goToRoom, router], + [router, isEmbedded, goToRoom], ); return ( diff --git a/apps/meteor/client/components/NotFoundState.tsx b/apps/meteor/client/components/NotFoundState.tsx index 2e6653f41e74..421fa1a9ca87 100644 --- a/apps/meteor/client/components/NotFoundState.tsx +++ b/apps/meteor/client/components/NotFoundState.tsx @@ -1,5 +1,5 @@ import { Box, States, StatesAction, StatesActions, StatesIcon, StatesSubtitle, StatesTitle } from '@rocket.chat/fuselage'; -import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -10,10 +10,10 @@ type NotFoundProps = { const NotFoundState = ({ title, subtitle }: NotFoundProps): ReactElement => { const t = useTranslation(); - const homeRoute = useRoute('home'); + const router = useRouter(); - const handleGoHomeClick = (): void => { - homeRoute.push(); + const handleGoHomeClick = () => { + router.navigate('/home'); }; return ( diff --git a/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx b/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx index 32ed00f4ccc3..3f08bcb121cb 100644 --- a/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx +++ b/apps/meteor/client/components/Sidebar/SidebarItemsAssembler.tsx @@ -22,7 +22,6 @@ const SidebarItemsAssembler: FC<SidebarItemsAssemblerProps> = ({ items, currentP {isSidebarItem(props) ? ( <SidebarNavigationItem permissionGranted={props.permissionGranted} - pathGroup={props.pathGroup || ''} pathSection={props.href ?? props.pathSection ?? ''} icon={props.icon} label={t((props.i18nLabel || props.name) as Parameters<typeof t>[0])} diff --git a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx index c596ce5d4515..ed6cd3e91d3b 100644 --- a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx +++ b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx @@ -1,14 +1,12 @@ import { Box, Icon, Tag } from '@rocket.chat/fuselage'; import type { IconProps } from '@rocket.chat/fuselage'; -import { useRoutePath } from '@rocket.chat/ui-contexts'; import type { FC, ReactElement } from 'react'; -import React, { memo, useMemo } from 'react'; +import React, { memo } from 'react'; import SidebarGenericItem from './SidebarGenericItem'; type SidebarNavigationItemProps = { permissionGranted?: (() => boolean) | boolean; - pathGroup: string; pathSection: string; icon?: IconProps['name']; label?: string; @@ -20,7 +18,6 @@ type SidebarNavigationItemProps = { const SidebarNavigationItem: FC<SidebarNavigationItemProps> = ({ permissionGranted, - pathGroup, pathSection, icon, label, @@ -30,9 +27,7 @@ const SidebarNavigationItem: FC<SidebarNavigationItemProps> = ({ // eslint-disable-next-line @typescript-eslint/naming-convention badge: Badge, }) => { - const params = useMemo(() => ({ group: pathGroup }), [pathGroup]); - const routePath = useRoutePath(pathSection, params); - const path = externalUrl ? pathSection : routePath; + const path = pathSection; const isActive = !!path && currentPath?.includes(path as string); if (permissionGranted === false || (typeof permissionGranted === 'function' && !permissionGranted())) { diff --git a/apps/meteor/client/hooks/useAnalyticsEventTracking.ts b/apps/meteor/client/hooks/useAnalyticsEventTracking.ts new file mode 100644 index 000000000000..78e078ef0070 --- /dev/null +++ b/apps/meteor/client/hooks/useAnalyticsEventTracking.ts @@ -0,0 +1,258 @@ +import { useRouter, useSetting, useUserId } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +import { callbacks } from '../../lib/callbacks'; + +function trackEvent(category: string, action: string, label?: unknown) { + const { _paq, ga } = window; + + _paq?.push(['trackEvent', category, action, label]); + ga?.('send', 'event', category, action, label); +} + +export const useAnalyticsEventTracking = () => { + const router = useRouter(); + + useEffect( + () => + router.subscribeToRouteChange(() => { + const { _paq, ga } = window; + + if (_paq) { + const http = location.protocol; + const slashes = http.concat('//'); + const host = slashes.concat(location.hostname); + _paq.push(['setCustomUrl', host + router.getLocationPathname()]); + _paq.push(['trackPageView']); + } + + ga?.('send', 'pageview', router.getLocationPathname()); + }), + [router], + ); + + useEffect(() => { + callbacks.add( + 'loginPageStateChange', + (state) => { + trackEvent('Navigation', 'Login Page State Change', state); + }, + callbacks.priority.MEDIUM, + 'analytics-login-state-change', + ); + + return () => { + callbacks.remove('loginPageStateChange', 'analytics-login-state-change'); + }; + }, []); + + const featuresMessages = useSetting('Analytics_features_messages', true); + + useEffect(() => { + if (!featuresMessages) { + return; + } + + callbacks.add( + 'afterSaveMessage', + (_message, room, _uid) => { + trackEvent('Message', 'Send', `${room.name} (${room._id})`); + }, + callbacks.priority.LOW, + 'trackEvents', + ); + + return () => { + callbacks.remove('afterSaveMessage', 'trackEvents'); + }; + }, [featuresMessages]); + + const featuresRooms = useSetting('Analytics_features_rooms', true); + + useEffect(() => { + if (!featuresRooms) { + return; + } + + callbacks.add( + 'afterCreateChannel', + (_owner, room) => { + trackEvent('Room', 'Create', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-after-create-channel', + ); + + callbacks.add( + 'roomNameChanged', + (room) => { + trackEvent('Room', 'Changed Name', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-room-name-changed', + ); + + callbacks.add( + 'roomTopicChanged', + (room) => { + trackEvent('Room', 'Changed Topic', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-room-topic-changed', + ); + + callbacks.add( + 'roomAnnouncementChanged', + (room) => { + trackEvent('Room', 'Changed Announcement', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-room-announcement-changed', + ); + + callbacks.add( + 'roomTypeChanged', + (room) => { + trackEvent('Room', 'Changed Room Type', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-room-type-changed', + ); + + callbacks.add( + 'archiveRoom', + (room) => { + trackEvent('Room', 'Archived', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-archive-room', + ); + + callbacks.add( + 'unarchiveRoom', + (room) => { + trackEvent('Room', 'Unarchived', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-unarchive-room', + ); + + callbacks.add( + 'roomAvatarChanged', + (room) => { + trackEvent('Room', 'Changed Avatar', `${room.name} (${room._id})`); + }, + callbacks.priority.MEDIUM, + 'analytics-room-avatar-changed', + ); + + return () => { + callbacks.remove('afterCreateChannel', 'analytics-after-create-channel'); + callbacks.remove('roomNameChanged', 'analytics-room-name-changed'); + callbacks.remove('roomTopicChanged', 'analytics-room-topic-changed'); + callbacks.remove('roomAnnouncementChanged', 'analytics-room-announcement-changed'); + callbacks.remove('roomTypeChanged', 'analytics-room-type-changed'); + callbacks.remove('archiveRoom', 'analytics-archive-room'); + callbacks.remove('unarchiveRoom', 'analytics-unarchive-room'); + callbacks.remove('roomAvatarChanged', 'analytics-room-avatar-changed'); + }; + }, [featuresRooms]); + + const featuresUsers = useSetting('Analytics_features_users', true); + + useEffect(() => { + if (!featuresUsers) { + return; + } + + callbacks.add( + 'userRegistered', + () => { + trackEvent('User', 'Registered'); + }, + callbacks.priority.MEDIUM, + 'piwik-user-resitered', + ); + + callbacks.add( + 'usernameSet', + () => { + trackEvent('User', 'Username Set'); + }, + callbacks.priority.MEDIUM, + 'piweik-username-set', + ); + + callbacks.add( + 'userPasswordReset', + () => { + trackEvent('User', 'Reset Password'); + }, + callbacks.priority.MEDIUM, + 'piwik-user-password-reset', + ); + + callbacks.add( + 'userConfirmationEmailRequested', + () => { + trackEvent('User', 'Confirmation Email Requested'); + }, + callbacks.priority.MEDIUM, + 'piwik-user-confirmation-email-requested', + ); + + callbacks.add( + 'userForgotPasswordEmailRequested', + () => { + trackEvent('User', 'Forgot Password Email Requested'); + }, + callbacks.priority.MEDIUM, + 'piwik-user-forgot-password-email-requested', + ); + + callbacks.add( + 'userStatusManuallySet', + (status) => { + trackEvent('User', 'Status Manually Changed', status); + }, + callbacks.priority.MEDIUM, + 'analytics-user-status-manually-set', + ); + + callbacks.add( + 'userAvatarSet', + (service) => { + trackEvent('User', 'Avatar Changed', service); + }, + callbacks.priority.MEDIUM, + 'analytics-user-avatar-set', + ); + + return () => { + callbacks.remove('userRegistered', 'piwik-user-resitered'); + callbacks.remove('usernameSet', 'piweik-username-set'); + callbacks.remove('userPasswordReset', 'piwik-user-password-reset'); + callbacks.remove('userConfirmationEmailRequested', 'piwik-user-confirmation-email-requested'); + callbacks.remove('userForgotPasswordEmailRequested', 'piwik-user-forgot-password-email-requested'); + callbacks.remove('userStatusManuallySet', 'analytics-user-status-manually-set'); + callbacks.remove('userAvatarSet', 'analytics-user-avatar-set'); + }; + }, [featuresUsers]); + + const uid = useUserId(); + + useEffect(() => { + if (!featuresUsers) { + return; + } + + const { _paq } = window; + + trackEvent('User', 'Login', uid); + _paq?.push(['setUserId', uid]); + + return () => { + trackEvent('User', 'Logout', uid); + }; + }, [featuresUsers, uid]); +}; diff --git a/apps/meteor/client/hooks/useDefaultRoute.ts b/apps/meteor/client/hooks/useDefaultRoute.ts deleted file mode 100644 index 9ed3e23d0b0f..000000000000 --- a/apps/meteor/client/hooks/useDefaultRoute.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useRoute } from '@rocket.chat/ui-contexts'; - -import type { Item, SidebarDivider, SidebarItem } from '../lib/createSidebarItems'; - -const isSidebarDivider = (sidebarItem: SidebarItem): sidebarItem is SidebarDivider => { - return (sidebarItem as SidebarDivider).divider === true; -}; - -const firstSidebarPage = (sidebarItem: SidebarItem): sidebarItem is Item => { - if (isSidebarDivider(sidebarItem)) { - return false; - } - - return Boolean(sidebarItem.permissionGranted?.()); -}; - -export const useDefaultRoute = (getSidebarItems: () => SidebarItem[], fallbackRoute: string): ReturnType<typeof useRoute> => { - const defaultRouteHref = getSidebarItems().find(firstSidebarPage)?.href || fallbackRoute; - const defaultRoute = useRoute(defaultRouteHref); - - return defaultRoute; -}; diff --git a/apps/meteor/client/hooks/useEmbeddedLayout.ts b/apps/meteor/client/hooks/useEmbeddedLayout.ts index d326e221eb94..1f411377b3e0 100644 --- a/apps/meteor/client/hooks/useEmbeddedLayout.ts +++ b/apps/meteor/client/hooks/useEmbeddedLayout.ts @@ -1,3 +1,3 @@ -import { useQueryStringParameter } from '@rocket.chat/ui-contexts'; +import { useSearchParameter } from '@rocket.chat/ui-contexts'; -export const useEmbeddedLayout = (): boolean => useQueryStringParameter('layout') === 'embedded'; +export const useEmbeddedLayout = (): boolean => useSearchParameter('layout') === 'embedded'; diff --git a/apps/meteor/client/importPackages.ts b/apps/meteor/client/importPackages.ts index 4f404eb143fb..eb2852f3fc94 100644 --- a/apps/meteor/client/importPackages.ts +++ b/apps/meteor/client/importPackages.ts @@ -1,6 +1,5 @@ import '../app/cors/client'; import '../app/2fa/client'; -import '../app/analytics/client'; import '../app/apple/client'; import '../app/authorization/client'; import '../app/autotranslate/client'; diff --git a/apps/meteor/client/lib/appLayout.tsx b/apps/meteor/client/lib/appLayout.tsx index 6a2fac6df44a..d189c7ce1c82 100644 --- a/apps/meteor/client/lib/appLayout.tsx +++ b/apps/meteor/client/lib/appLayout.tsx @@ -22,20 +22,24 @@ class AppLayoutSubscription extends Emitter<{ update: void }> { } render(element: ReactElement): void { - this.setCurrentValue( + this.setCurrentValue(this.wrap(element)); + } + + renderStandalone(element: ReactElement): void { + this.setCurrentValue(element); + } + + wrap(element: ReactElement): ReactElement { + return ( <> <ConnectionStatusBar /> <BannerRegion /> {element} <PortalsWrapper /> <ModalRegion /> - </>, + </> ); } - - renderStandalone(element: ReactElement): void { - this.setCurrentValue(element); - } } export const appLayout = new AppLayoutSubscription(); diff --git a/apps/meteor/client/lib/chats/flows/replyBroadcast.ts b/apps/meteor/client/lib/chats/flows/replyBroadcast.ts index ad7be7ec989d..69e857ee57cb 100644 --- a/apps/meteor/client/lib/chats/flows/replyBroadcast.ts +++ b/apps/meteor/client/lib/chats/flows/replyBroadcast.ts @@ -1,17 +1,15 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { FlowRouter } from 'meteor/kadira:flow-router'; +import { router } from '../../../providers/RouterProvider'; import { roomCoordinator } from '../../rooms/roomCoordinator'; import type { ChatAPI } from '../ChatAPI'; export const replyBroadcast = async (_chat: ChatAPI, message: IMessage) => { - const queryStringParams = FlowRouter.current().queryParams; - roomCoordinator.openRouteLink( 'd', { name: message.u.username }, { - ...queryStringParams, + ...router.getSearchParameters(), reply: message._id, }, ); diff --git a/apps/meteor/client/lib/createRouteGroup.tsx b/apps/meteor/client/lib/createRouteGroup.tsx index 8f8691f82966..e418ecc56e99 100644 --- a/apps/meteor/client/lib/createRouteGroup.tsx +++ b/apps/meteor/client/lib/createRouteGroup.tsx @@ -1,153 +1,81 @@ -import type { Context, Current, Group, RouteOptions } from 'meteor/kadira:flow-router'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; -import type { ElementType, ReactNode } from 'react'; -import React from 'react'; +import { type IRouterPaths, type RouteName, type RouterPathPattern } from '@rocket.chat/ui-contexts'; +import React, { type ElementType, type ReactNode } from 'react'; +import { router } from '../providers/RouterProvider'; import MainLayout from '../views/root/MainLayout'; import { appLayout } from './appLayout'; -let oldRoute: Current; +type GroupName = 'omnichannel' | 'marketplace' | 'account' | 'admin'; -const registerLazyComponentRoute = ( - routeGroup: Group, - RouterComponent: ElementType<{ - children?: ReactNode; - }>, - path: string, - { - component: RouteComponent, - props, - ready = true, - ...rest - }: RouteOptions & { - component: ElementType; - props?: Record<string, unknown>; - ready?: boolean; - }, -): [register: () => void, unregister: () => void] => { - const enabled = new ReactiveVar(ready ? true : undefined); - let computation: Tracker.Computation | undefined; - - const handleEnter = (_context: Context, _redirect: (pathDef: string) => void, stop: () => void): void => { - const _enabled = Tracker.nonreactive(() => enabled.get()); - if (_enabled === false) { - stop(); - return; - } +type GroupPrefix<TGroupName extends GroupName> = IRouterPaths[`${TGroupName}-index`]['pattern']; - computation = Tracker.autorun(() => { - const _enabled = enabled.get(); - - if (_enabled === false) { - FlowRouter.go('/'); - } - }); - }; - - const handleExit = (context: Context): void => { - computation?.stop(); - - if (!context.oldRoute) { - return; - } +type RouteNamesOf<TGroupName extends GroupName> = Extract< + | keyof { + [TRouteName in RouteName as IRouterPaths[TRouteName]['pattern'] extends `${GroupPrefix<TGroupName>}/${string}` + ? TRouteName + : never]: never; + } + | `${GroupName}-index`, + RouteName +>; - if (context.route.group?.name === context.oldRoute?.group?.name) { - return; - } - - oldRoute?.route?.name && FlowRouter.go(oldRoute.route.name, oldRoute.params, oldRoute.queryParams); - }; - - routeGroup.route(path, { - ...rest, - triggersEnter: [handleEnter, ...(rest.triggersEnter ?? [])], - triggersExit: [handleExit, ...(rest.triggersExit ?? [])], - action() { - appLayout.render( - <MainLayout> - <RouterComponent> - <RouteComponent {...props} /> - </RouterComponent> - </MainLayout>, - ); - }, - }); - - return [(): void => enabled.set(true), (): void => enabled.set(false)]; -}; +type TrimPrefix<T extends string, P extends string> = T extends `${P}${infer U}` ? U : T; -export const createRouteGroup = ( - name: string, - prefix: string, +export const createRouteGroup = <TGroupName extends GroupName>( + name: TGroupName, + prefix: GroupPrefix<TGroupName>, RouterComponent: ElementType<{ children?: ReactNode; }>, -): { - ( - path: string, - options: RouteOptions & { - component: ElementType; - props?: Record<string, unknown>; - ready?: boolean; +) => { + router.defineRoutes([ + { + path: prefix, + id: `${name}-index`, + element: appLayout.wrap( + <MainLayout> + <RouterComponent /> + </MainLayout>, + ), }, - ): [register: () => void, unregister: () => void]; - (path: string, options: RouteOptions): void; -} => { - const routeGroup = FlowRouter.group({ - name, - prefix, - }); - - function registerRoute( - path: string, - options: RouteOptions & { + ]); + + return <TRouteName extends RouteNamesOf<TGroupName>>( + path: TrimPrefix<IRouterPaths[TRouteName]['pattern'], GroupPrefix<TGroupName>>, + { + name, + component: RouteComponent, + props, + ready = true, + }: { + name: TRouteName; component: ElementType; props?: Record<string, unknown>; ready?: boolean; }, - ): [register: () => void, unregister: () => void]; - function registerRoute(path: string, options: RouteOptions): void; - function registerRoute( - path: string, - options: - | RouteOptions - | (RouteOptions & { - component: ElementType; - props?: Record<string, unknown>; - ready?: boolean; - }), - ): [register: () => void, unregister: () => void] | void { - if ('component' in options) { - return registerLazyComponentRoute(routeGroup, RouterComponent, path, options); - } - - routeGroup.route(path, options); - } + ): [register: () => void, unregister: () => void] => { + let unregister: (() => void) | undefined; + + const register = () => { + unregister = router.defineRoutes([ + { + path: `${prefix}${path}` as RouterPathPattern, + id: name, + element: appLayout.wrap( + <MainLayout> + <RouterComponent> + <RouteComponent {...props} /> + </RouterComponent> + </MainLayout>, + ), + }, + ]); + }; - registerRoute('/', { - name: `${name}-index`, - action() { - appLayout.render( - <MainLayout> - <RouterComponent /> - </MainLayout>, - ); - }, - }); + if (ready) { + register(); + } - return registerRoute; + return [register, () => unregister?.()]; + }; }; - -Tracker.autorun( - (() => { - let oldName: string; - return () => { - const name = FlowRouter.getRouteName(); - if (oldName !== name) { - oldRoute = FlowRouter.current(); - } - }; - })(), -); diff --git a/apps/meteor/client/lib/createSidebarItems.ts b/apps/meteor/client/lib/createSidebarItems.ts index 7ce4a5250155..8d836cc293ce 100644 --- a/apps/meteor/client/lib/createSidebarItems.ts +++ b/apps/meteor/client/lib/createSidebarItems.ts @@ -1,14 +1,14 @@ import type { IconProps } from '@rocket.chat/fuselage'; +import type { LocationPathname } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; export type Item = { i18nLabel: string; - href?: string; + href?: LocationPathname | `https://go.rocket.chat/i/${string}`; icon?: IconProps['name']; tag?: 'Alpha' | 'Beta'; permissionGranted?: () => boolean; pathSection?: string; - pathGroup?: string; name?: string; externalUrl?: boolean; badge?: () => ReactElement; @@ -17,6 +17,9 @@ export type SidebarDivider = { divider: boolean; i18nLabel: string }; export type SidebarItem = Item | SidebarDivider; export const isSidebarItem = (item: SidebarItem): item is Item => !('divider' in item); +export const isGoRocketChatLink = (link: string): link is `https://go.rocket.chat/i/${string}` => + link.startsWith('https://go.rocket.chat/i/'); + export const createSidebarItems = ( initialItems: SidebarItem[] = [], ): { diff --git a/apps/meteor/client/lib/errors/VisitorDoesNotExistError.ts b/apps/meteor/client/lib/errors/VisitorDoesNotExistError.ts new file mode 100644 index 000000000000..646e17b17ff9 --- /dev/null +++ b/apps/meteor/client/lib/errors/VisitorDoesNotExistError.ts @@ -0,0 +1,7 @@ +import { RocketChatError } from './RocketChatError'; + +export class VisitorDoesNotExistError extends RocketChatError<'visitor-does-not-exist'> { + constructor(message = 'Visitor does not exist', details?: string) { + super('visitor-does-not-exist', message, details); + } +} diff --git a/apps/meteor/client/lib/rooms/roomCoordinator.tsx b/apps/meteor/client/lib/rooms/roomCoordinator.tsx index a09d2a9bcdc2..9a2e3d14ab47 100644 --- a/apps/meteor/client/lib/rooms/roomCoordinator.tsx +++ b/apps/meteor/client/lib/rooms/roomCoordinator.tsx @@ -1,6 +1,7 @@ import type { IRoom, RoomType, IUser, AtLeast, ValueOf, ISubscription } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import { FlowRouter } from 'meteor/kadira:flow-router'; +import type { RouteName } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import React from 'react'; import { hasPermission } from '../../../app/authorization/client'; @@ -16,7 +17,8 @@ import type { IRoomTypeClientConfig, } from '../../../definition/IRoomTypeConfig'; import { RoomCoordinator } from '../../../lib/rooms/coordinator'; -import RoomOpener from '../../views/room/RoomOpener'; +import { router } from '../../providers/RouterProvider'; +import RoomRoute from '../../views/room/RoomRoute'; import MainLayout from '../../views/root/MainLayout/MainLayout'; import { appLayout } from '../appLayout'; @@ -70,7 +72,12 @@ class RoomCoordinatorClient extends RoomCoordinator { return this.roomTypes[roomType].directives as IRoomTypeClientDirectives; } - public openRouteLink(roomType: RoomType, subData: RoomIdentification, queryParams?: Record<string, string>): void { + public openRouteLink( + roomType: RoomType, + subData: RoomIdentification, + queryParams?: Record<string, string>, + options: { replace?: boolean } = {}, + ): void { const config = this.getRoomTypeConfig(roomType); if (!config?.route) { return; @@ -87,7 +94,14 @@ class RoomCoordinatorClient extends RoomCoordinator { return; } - FlowRouter.go(config.route.name, routeData, queryParams); + router.navigate( + { + pattern: config.route.path ?? '/home', + params: routeData, + search: queryParams, + }, + options, + ); } public isLivechatRoom(roomType: string): boolean { @@ -161,7 +175,7 @@ class RoomCoordinatorClient extends RoomCoordinator { return true; } - private validateRoute(route: IRoomTypeRouteConfig): void { + private validateRoute<TRouteName extends RouteName>(route: IRoomTypeRouteConfig<TRouteName>): void { const { name, path, link } = route; if (typeof name !== 'string' || name.length === 0) { @@ -199,18 +213,17 @@ class RoomCoordinatorClient extends RoomCoordinator { route: { name, path }, } = roomConfig; const { extractOpenRoomParams } = directives; - FlowRouter.route(path, { - name, - action: (params) => { - const { type, ref } = extractOpenRoomParams(params ?? {}); - - appLayout.render( + router.defineRoutes([ + { + path, + id: name, + element: appLayout.wrap( <MainLayout> - <RoomOpener type={type} reference={ref} /> + <RoomRoute extractOpenRoomParams={extractOpenRoomParams} /> </MainLayout>, - ); + ), }, - }); + ]); } } @@ -225,7 +238,12 @@ class RoomCoordinatorClient extends RoomCoordinator { return false; } - return FlowRouter.url(config.route.name, routeData); + return Meteor.absoluteUrl( + router.buildRoutePath({ + name: config.route.name, + params: routeData, + }), + ); } public isRouteNameKnown(routeName: string): boolean { diff --git a/apps/meteor/client/lib/rooms/roomTypes/direct.ts b/apps/meteor/client/lib/rooms/roomTypes/direct.ts index 575ec1534b91..776c309c8544 100644 --- a/apps/meteor/client/lib/rooms/roomTypes/direct.ts +++ b/apps/meteor/client/lib/rooms/roomTypes/direct.ts @@ -142,7 +142,7 @@ roomCoordinator.add( }, extractOpenRoomParams({ rid }) { - return { type: 'd', ref: rid }; + return { type: 'd', reference: rid }; }, findRoom(identifier) { diff --git a/apps/meteor/client/lib/rooms/roomTypes/livechat.ts b/apps/meteor/client/lib/rooms/roomTypes/livechat.ts index 82f2a48a9a9f..26919ac83b99 100644 --- a/apps/meteor/client/lib/rooms/roomTypes/livechat.ts +++ b/apps/meteor/client/lib/rooms/roomTypes/livechat.ts @@ -81,7 +81,7 @@ roomCoordinator.add( }, extractOpenRoomParams({ id }) { - return { type: 'l', ref: id }; + return { type: 'l', reference: id }; }, } as AtLeast<IRoomTypeClientDirectives, 'roomName'>, ); diff --git a/apps/meteor/client/lib/rooms/roomTypes/private.ts b/apps/meteor/client/lib/rooms/roomTypes/private.ts index b1d34a92c4ad..b99e172e8396 100644 --- a/apps/meteor/client/lib/rooms/roomTypes/private.ts +++ b/apps/meteor/client/lib/rooms/roomTypes/private.ts @@ -106,7 +106,7 @@ roomCoordinator.add( }, extractOpenRoomParams({ name }) { - return { type: 'p', ref: name }; + return { type: 'p', reference: name }; }, findRoom(identifier) { diff --git a/apps/meteor/client/lib/rooms/roomTypes/public.ts b/apps/meteor/client/lib/rooms/roomTypes/public.ts index 4dd38eb95881..cf9d4c1a978c 100644 --- a/apps/meteor/client/lib/rooms/roomTypes/public.ts +++ b/apps/meteor/client/lib/rooms/roomTypes/public.ts @@ -105,7 +105,7 @@ roomCoordinator.add( }, extractOpenRoomParams({ name }) { - return { type: 'c', ref: name }; + return { type: 'c', reference: name }; }, findRoom(identifier) { diff --git a/apps/meteor/client/lib/rooms/roomTypes/voip.ts b/apps/meteor/client/lib/rooms/roomTypes/voip.ts index 258103651e0f..30b07aaec1ee 100644 --- a/apps/meteor/client/lib/rooms/roomTypes/voip.ts +++ b/apps/meteor/client/lib/rooms/roomTypes/voip.ts @@ -45,7 +45,7 @@ roomCoordinator.add( }, extractOpenRoomParams({ id }) { - return { type: 'v', ref: id }; + return { type: 'v', reference: id }; }, } as AtLeast<IRoomTypeClientDirectives, 'roomName'>, ); diff --git a/apps/meteor/client/lib/tracker.ts b/apps/meteor/client/lib/tracker.ts index 5c069ec4da4d..2e4e6aab89f4 100644 --- a/apps/meteor/client/lib/tracker.ts +++ b/apps/meteor/client/lib/tracker.ts @@ -6,12 +6,10 @@ export const asReactiveSource = <T>(subscribe: (cb: () => void) => () => void, g } const computation = Tracker.currentComputation; - const unsubscribe = subscribe(() => computation.invalidate()); - // const id = new Error().stack?.split('\n')[2].trim(); - // console.log('sub', id); - computation.onInvalidate(() => { + const unsubscribe = subscribe(() => computation?.invalidate()); + + computation?.onInvalidate(() => { unsubscribe(); - // console.log('unsub', id); }); return getSnapshot(); diff --git a/apps/meteor/client/lib/userData.ts b/apps/meteor/client/lib/userData.ts index ba6d62303280..9e67f4034b1d 100644 --- a/apps/meteor/client/lib/userData.ts +++ b/apps/meteor/client/lib/userData.ts @@ -46,7 +46,7 @@ const updateUser = (userData: IUser): void => { Object.keys(user).forEach((key) => { delete userData[key as keyof IUser]; }); - Users.update({ _id: user._id }, { $set: userData }); + Users.update({ _id: user._id }, { $set: { ...userData } }); }; let cancel: undefined | (() => void); @@ -69,7 +69,7 @@ export const synchronizeUserData = async (uid: IUser['_id']): Promise<RawUserDat break; case 'updated': - Users.upsert({ _id: uid }, { $set: data.diff, $unset: data.unset }); + Users.upsert({ _id: uid }, { $set: data.diff, $unset: data.unset as any }); break; case 'removed': diff --git a/apps/meteor/client/lib/utils/goToRoomById.ts b/apps/meteor/client/lib/utils/goToRoomById.ts index d55d47e37a59..2bb5936d5510 100644 --- a/apps/meteor/client/lib/utils/goToRoomById.ts +++ b/apps/meteor/client/lib/utils/goToRoomById.ts @@ -1,8 +1,8 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { memoize } from '@rocket.chat/memo'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { ChatSubscription } from '../../../app/models/client'; +import { router } from '../../providers/RouterProvider'; import { roomCoordinator } from '../rooms/roomCoordinator'; import { callWithErrorHandling } from './callWithErrorHandling'; @@ -16,10 +16,10 @@ export const goToRoomById = async (rid: IRoom['_id']): Promise<void> => { const subscription: ISubscription | undefined = ChatSubscription.findOne({ rid }); if (subscription) { - roomCoordinator.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams); + roomCoordinator.openRouteLink(subscription.t, subscription, router.getSearchParameters()); return; } const room = await getRoomById(rid); - roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }, FlowRouter.current().queryParams); + roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }, router.getSearchParameters()); }; diff --git a/apps/meteor/client/lib/utils/isLayoutEmbedded.ts b/apps/meteor/client/lib/utils/isLayoutEmbedded.ts index 07743a49f72e..540ccf129603 100644 --- a/apps/meteor/client/lib/utils/isLayoutEmbedded.ts +++ b/apps/meteor/client/lib/utils/isLayoutEmbedded.ts @@ -1,3 +1,3 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; +import { router } from '../../providers/RouterProvider'; -export const isLayoutEmbedded = (): boolean => FlowRouter.getQueryParam('layout') === 'embedded'; +export const isLayoutEmbedded = (): boolean => router.getSearchParameters().layout === 'embedded'; diff --git a/apps/meteor/client/lib/utils/legacyJumpToMessage.ts b/apps/meteor/client/lib/utils/legacyJumpToMessage.ts index 580224444f54..3888e672bdc7 100644 --- a/apps/meteor/client/lib/utils/legacyJumpToMessage.ts +++ b/apps/meteor/client/lib/utils/legacyJumpToMessage.ts @@ -1,9 +1,9 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { isThreadMessage } from '@rocket.chat/core-typings'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { ChatRoom } from '../../../app/models/client'; import { RoomHistoryManager } from '../../../app/ui-utils/client'; +import { router } from '../../providers/RouterProvider'; import { RoomManager } from '../RoomManager'; import { goToRoomById } from './goToRoomById'; @@ -14,24 +14,27 @@ export const legacyJumpToMessage = async (message: IMessage) => { } if (isThreadMessage(message) || message.tcount) { - const { route, queryParams, params } = FlowRouter.current(); + const { tab, context } = router.getRouteParameters(); - if (params.tab === 'thread' && (params.context === message.tmid || params.context === message._id)) { + if (tab === 'thread' && (context === message.tmid || context === message._id)) { return; } - FlowRouter.go( - route?.name ?? '/', + router.navigate( { - tab: 'thread', - context: message.tmid || message._id, - rid: message.rid, - name: ChatRoom.findOne({ _id: message.rid })?.name ?? '', - }, - { - ...queryParams, - msg: message._id, + name: router.getRouteName()!, + params: { + tab: 'thread', + context: message.tmid || message._id, + rid: message.rid, + name: ChatRoom.findOne({ _id: message.rid })?.name ?? '', + }, + search: { + ...router.getSearchParameters(), + msg: message._id, + }, }, + { replace: false }, ); return; } diff --git a/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts b/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts index fbb8794e6b40..74055930ef3b 100644 --- a/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts +++ b/apps/meteor/client/lib/utils/setMessageJumpQueryStringParameter.ts @@ -1,6 +1,15 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { FlowRouter } from 'meteor/kadira:flow-router'; + +import { router } from '../../providers/RouterProvider'; export const setMessageJumpQueryStringParameter = async (msg: IMessage['_id'] | null) => { - FlowRouter.setQueryParams({ msg }); + const { msg: _, ...search } = router.getSearchParameters(); + + router.navigate( + { + pathname: router.getLocationPathname(), + search: msg ? { ...search, msg } : search, + }, + { replace: true }, + ); }; diff --git a/apps/meteor/client/providers/CallProvider/CallProvider.tsx b/apps/meteor/client/providers/CallProvider/CallProvider.tsx index 22ef506ad481..0de0a3fda0c3 100644 --- a/apps/meteor/client/providers/CallProvider/CallProvider.tsx +++ b/apps/meteor/client/providers/CallProvider/CallProvider.tsx @@ -13,7 +13,7 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { Random } from '@rocket.chat/random'; import type { Device, IExperimentalHTMLAudioElement } from '@rocket.chat/ui-contexts'; import { - useRoute, + useRouter, useUser, useSetting, useEndpoint, @@ -23,7 +23,6 @@ import { useSetModal, useTranslation, } from '@rocket.chat/ui-contexts'; -// import { useRoute, useUser, useSetting, useEndpoint, useStream, useSetModal } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { useMemo, useRef, useCallback, useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; @@ -56,7 +55,7 @@ export const CallProvider: FC = ({ children }) => { const result = useVoipClient(); const user = useUser(); - const homeRoute = useRoute('home'); + const router = useRouter(); const setOutputMediaDevice = useSetOutputMediaDevice(); const setInputMediaDevice = useSetInputMediaDevice(); @@ -80,14 +79,14 @@ export const CallProvider: FC = ({ children }) => { token: roomInfo.v.token || '', options: { comment: data?.comment, tags: data?.tags }, })); - homeRoute.push({}); + router.navigate('/home'); const queueAggregator = result.voipClient?.getAggregator(); if (queueAggregator) { queueAggregator.callEnded(); } }, - [homeRoute, result?.voipClient, roomInfo, voipCloseRoomEndpoint], + [router, result?.voipClient, roomInfo, voipCloseRoomEndpoint], ); const openWrapUpModal = useCallback((): void => { diff --git a/apps/meteor/client/providers/LayoutProvider.tsx b/apps/meteor/client/providers/LayoutProvider.tsx index 85a33fab7a23..95b6b04f9f53 100644 --- a/apps/meteor/client/providers/LayoutProvider.tsx +++ b/apps/meteor/client/providers/LayoutProvider.tsx @@ -1,12 +1,12 @@ import { useBreakpoints } from '@rocket.chat/fuselage-hooks'; -import { LayoutContext, useQueryStringParameter, useRoute, useSetting } from '@rocket.chat/ui-contexts'; +import { LayoutContext, useRouter, useSearchParameter, useSetting } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { useMemo, useState, useEffect } from 'react'; const LayoutProvider: FC = ({ children }) => { const showTopNavbarEmbeddedLayout = Boolean(useSetting('UI_Show_top_navbar_embedded_layout')); const [isCollapsed, setIsCollapsed] = useState(false); - const layout = useQueryStringParameter('layout'); + const layout = useSearchParameter('layout'); const isEmbedded = layout === 'embedded'; const breakpoints = useBreakpoints(); // ["xs", "sm", "md", "lg", "xl", xxl"] @@ -16,7 +16,7 @@ const LayoutProvider: FC = ({ children }) => { setIsCollapsed(isMobile); }, [isMobile]); - const routeHome = useRoute('home'); + const router = useRouter(); return ( <LayoutContext.Provider @@ -31,7 +31,7 @@ const LayoutProvider: FC = ({ children }) => { toggle: () => setIsCollapsed((isCollapsed) => !isCollapsed), collapse: () => setIsCollapsed(true), expand: () => setIsCollapsed(false), - close: () => (isEmbedded ? setIsCollapsed(true) : routeHome.push()), + close: () => (isEmbedded ? setIsCollapsed(true) : router.navigate('/home')), }, size: { sidebar: '240px', @@ -42,7 +42,7 @@ const LayoutProvider: FC = ({ children }) => { // eslint-disable-next-line no-nested-ternary contextualBarPosition: breakpoints.includes('sm') ? (breakpoints.includes('lg') ? 'relative' : 'absolute') : 'fixed', }), - [isMobile, isEmbedded, showTopNavbarEmbeddedLayout, isCollapsed, breakpoints, routeHome], + [isMobile, isEmbedded, showTopNavbarEmbeddedLayout, isCollapsed, breakpoints, router], )} /> ); diff --git a/apps/meteor/client/providers/RouterProvider.tsx b/apps/meteor/client/providers/RouterProvider.tsx index 66bb0aedf93b..af0448ce34e8 100644 --- a/apps/meteor/client/providers/RouterProvider.tsx +++ b/apps/meteor/client/providers/RouterProvider.tsx @@ -1,88 +1,208 @@ -import type { RouterContextValue } from '@rocket.chat/ui-contexts'; +import type { + RouterContextValue, + RouteName, + LocationPathname, + RouteParameters, + SearchParameters, + To, + RouteObject, +} from '@rocket.chat/ui-contexts'; import { RouterContext } from '@rocket.chat/ui-contexts'; +import type { LocationSearch } from '@rocket.chat/ui-contexts/src/RouterContext'; import { FlowRouter } from 'meteor/kadira:flow-router'; import { Tracker } from 'meteor/tracker'; import type { FC } from 'react'; import React from 'react'; -import { createSubscription } from '../lib/createSubscription'; - -const queryRoutePath = ( - name: Parameters<RouterContextValue['queryRoutePath']>[0], - parameters: Parameters<RouterContextValue['queryRoutePath']>[1], - queryStringParameters: Parameters<RouterContextValue['queryRoutePath']>[2], -): ReturnType<RouterContextValue['queryRoutePath']> => createSubscription(() => FlowRouter.path(name, parameters, queryStringParameters)); - -const queryRouteUrl = ( - name: Parameters<RouterContextValue['queryRouteUrl']>[0], - parameters: Parameters<RouterContextValue['queryRouteUrl']>[1], - queryStringParameters: Parameters<RouterContextValue['queryRouteUrl']>[2], -): ReturnType<RouterContextValue['queryRouteUrl']> => createSubscription(() => FlowRouter.url(name, parameters, queryStringParameters)); - -const pushRoute = ( - name: Parameters<RouterContextValue['pushRoute']>[0], - parameters: Parameters<RouterContextValue['pushRoute']>[1], - queryStringParameters?: ((prev: Record<string, string>) => Record<string, string>) | Record<string, string>, -): ReturnType<RouterContextValue['pushRoute']> => { - const queryParams = - typeof queryStringParameters === 'function' ? queryStringParameters(FlowRouter.current().queryParams) : queryStringParameters; - FlowRouter.go(name, parameters, queryParams); +import { appLayout } from '../lib/appLayout'; +import { queueMicrotask } from '../lib/utils/queueMicrotask'; + +FlowRouter.wait(); + +FlowRouter.notFound = { + action: () => undefined, }; -const replaceRoute = ( - name: Parameters<RouterContextValue['replaceRoute']>[0], - parameters: Parameters<RouterContextValue['replaceRoute']>[1], - queryStringParameters?: ((prev: Record<string, string>) => Record<string, string>) | Record<string, string>, -): ReturnType<RouterContextValue['replaceRoute']> => { - FlowRouter.withReplaceState(() => { - const queryParams = - typeof queryStringParameters === 'function' ? queryStringParameters(FlowRouter.current().queryParams) : queryStringParameters; - FlowRouter.go(name, parameters, queryParams); - }); +const subscribers = new Set<() => void>(); + +const listenToRouteChange = () => { + FlowRouter.watchPathChange(); + subscribers.forEach((onRouteChange) => onRouteChange()); }; -const queryRouteParameter = ( - name: Parameters<RouterContextValue['replaceRoute']>[0], -): ReturnType<RouterContextValue['queryRouteParameter']> => createSubscription(() => FlowRouter.getParam(name)); +let computation: Tracker.Computation | undefined; -const queryQueryStringParameter = ( - name: Parameters<RouterContextValue['queryQueryStringParameter']>[0], -): ReturnType<RouterContextValue['queryQueryStringParameter']> => createSubscription(() => FlowRouter.getQueryParam(name)); +queueMicrotask(() => { + computation = Tracker.autorun(listenToRouteChange); +}); -const queryCurrentRoute = (): ReturnType<RouterContextValue['queryCurrentRoute']> => - createSubscription(() => { - FlowRouter.watchPathChange(); - const { route, params, queryParams } = FlowRouter.current(); - return [route?.name, params, queryParams, route?.group?.name]; - }); +const subscribeToRouteChange = (onRouteChange: () => void): (() => void) => { + subscribers.add(onRouteChange); + + computation?.invalidate(); + + return () => { + subscribers.delete(onRouteChange); + + if (subscribers.size === 0) { + queueMicrotask(() => computation?.stop()); + } + }; +}; + +const getLocationPathname = () => FlowRouter.current().path as LocationPathname; + +const getLocationSearch = () => location.search as LocationSearch; + +const getRouteParameters = () => (FlowRouter.current().params ?? {}) as RouteParameters; + +const getSearchParameters = () => (FlowRouter.current().queryParams ?? {}) as SearchParameters; + +const getRouteName = () => FlowRouter.current().route?.name as RouteName | undefined; + +const encodeSearchParameters = (searchParameters: SearchParameters) => { + const search = new URLSearchParams(); + + for (const [key, value] of Object.entries(searchParameters)) { + search.append(key, value); + } + + const searchString = search.toString(); + + return searchString ? `?${searchString}` : ''; +}; + +const buildRoutePath = (to: To): LocationPathname | `${LocationPathname}?${LocationSearch}` => { + if (typeof to === 'string') { + return to; + } + + if ('pathname' in to) { + const { pathname, search = {} } = to; + return (pathname + encodeSearchParameters(search)) as LocationPathname | `${LocationPathname}?${LocationSearch}`; + } + + if ('pattern' in to) { + const { pattern, params = {}, search = {} } = to; + return Tracker.nonreactive(() => FlowRouter.path(pattern, params, search)) as + | LocationPathname + | `${LocationPathname}?${LocationSearch}`; + } + + if ('name' in to) { + const { name, params = {}, search = {} } = to; + return Tracker.nonreactive(() => FlowRouter.path(name, params, search)) as LocationPathname | `${LocationPathname}?${LocationSearch}`; + } + + throw new Error('Invalid route'); +}; + +const navigate = ( + toOrDelta: To | number, + options?: { + replace?: boolean; + }, +) => { + if (typeof toOrDelta === 'number') { + history.go(toOrDelta); + return; + } + + const path = buildRoutePath(toOrDelta); + const state = { path }; -const setQueryString = (paramsOrFn: Record<string, string | null> | ((prev: Record<string, string>) => Record<string, string>)): void => { - if (typeof paramsOrFn === 'function') { - const prevParams = FlowRouter.current().queryParams; - const emptyParams = Object.fromEntries(Object.entries(prevParams).map(([key]) => [key, null])); - const newParams = paramsOrFn(prevParams); - FlowRouter.setQueryParams({ ...emptyParams, ...newParams }); + if (options?.replace) { + history.replaceState(state, '', path); + } else { + history.pushState(state, '', path); + } + + dispatchEvent(new PopStateEvent('popstate', { state })); +}; + +const routes: RouteObject[] = []; +const routesSubscribers = new Set<() => void>(); + +const updateFlowRouter = () => { + if (FlowRouter._initialized) { + FlowRouter._updateCallbacks(); + FlowRouter._page.dispatch({ path: FlowRouter._current.path, params: {} }); return; } - FlowRouter.setQueryParams(paramsOrFn); + FlowRouter.initialize(); +}; + +const defineRoutes = (routes: RouteObject[]) => { + const flowRoutes = routes.map((route) => { + if (route.path === '*') { + FlowRouter.notFound = { + action: () => appLayout.renderStandalone(<>{route.element}</>), + }; + + return FlowRouter.notFound; + } + + return FlowRouter.route(route.path, { + name: route.id, + action: () => appLayout.renderStandalone(<>{route.element}</>), + }); + }); + + routes.push(...routes); + const index = routes.length - 1; + + updateFlowRouter(); + routesSubscribers.forEach((onRoutesChange) => onRoutesChange()); + + return () => { + flowRoutes.forEach((flowRoute) => { + FlowRouter._routes = FlowRouter._routes.filter((r) => r !== flowRoute); + if ('name' in flowRoute && flowRoute.name) { + delete FlowRouter._routesMap[flowRoute.name]; + } else { + FlowRouter.notFound = { + action: () => appLayout.renderStandalone(<></>), + }; + } + }); + + if (index !== -1) { + routes.splice(index, 1); + } + + updateFlowRouter(); + routesSubscribers.forEach((onRoutesChange) => onRoutesChange()); + }; +}; + +const getRoutes = () => routes; + +const subscribeToRoutesChange = (onRoutesChange: () => void): (() => void) => { + routesSubscribers.add(onRoutesChange); + + onRoutesChange(); + + return () => { + routesSubscribers.delete(onRoutesChange); + }; }; -const getRoutePath = (name: string, parameters?: Record<string, string>, queryStringParameters?: Record<string, string>) => - Tracker.nonreactive(() => FlowRouter.path(name, parameters, queryStringParameters)); - -const contextValue = { - queryRoutePath, - queryRouteUrl, - pushRoute, - replaceRoute, - queryRouteParameter, - queryQueryStringParameter, - queryCurrentRoute, - setQueryString, - getRoutePath, +/** @deprecated */ +export const router: RouterContextValue = { + subscribeToRouteChange, + getLocationPathname, + getLocationSearch, + getRouteParameters, + getSearchParameters, + getRouteName, + buildRoutePath, + navigate, + defineRoutes, + getRoutes, + subscribeToRoutesChange, }; -const RouterProvider: FC = ({ children }) => <RouterContext.Provider children={children} value={contextValue} />; +const RouterProvider: FC = ({ children }) => <RouterContext.Provider children={children} value={router} />; export default RouterProvider; diff --git a/apps/meteor/client/sidebar/Item/Medium.tsx b/apps/meteor/client/sidebar/Item/Medium.tsx index 180787ba6041..16de09d151f8 100644 --- a/apps/meteor/client/sidebar/Item/Medium.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.tsx @@ -30,12 +30,12 @@ const Medium: VFC<MediumProps> = ({ icon, title = '', avatar, actions, href, bad }; return ( - <Sidebar.Item {...props} {...({ href } as any)} clickable={!!href}> + <Sidebar.Item {...props} href={href} clickable={!!href}> {avatar && <Sidebar.Item.Avatar>{avatar}</Sidebar.Item.Avatar>} <Sidebar.Item.Content> <Sidebar.Item.Wrapper> {icon} - <Sidebar.Item.Title data-qa='sidebar-item-title' className={(unread && 'rcx-sidebar-item--highlighted') as string}> + <Sidebar.Item.Title data-qa='sidebar-item-title' className={unread ? 'rcx-sidebar-item--highlighted' : undefined}> {title} </Sidebar.Item.Title> </Sidebar.Item.Wrapper> diff --git a/apps/meteor/client/sidebar/RoomMenu.tsx b/apps/meteor/client/sidebar/RoomMenu.tsx index 09903e66b59e..be8d889f3065 100644 --- a/apps/meteor/client/sidebar/RoomMenu.tsx +++ b/apps/meteor/client/sidebar/RoomMenu.tsx @@ -3,9 +3,9 @@ import { Option, Menu } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey, Fields } from '@rocket.chat/ui-contexts'; import { + useRouter, useSetModal, useToastMessageDispatch, - useRoute, useUserSubscription, useSetting, usePermission, @@ -77,7 +77,7 @@ const RoomMenu = ({ const closeModal = useMutableCallback(() => setModal()); - const router = useRoute('home'); + const router = useRouter(); const subscription = useUserSubscription(rid, fields); const canFavorite = useSetting('Favorite_Rooms'); @@ -115,7 +115,7 @@ const RoomMenu = ({ try { await leaveRoom({ roomId: rid }); if (roomOpen) { - router.push({}); + router.navigate('/home'); } LegacyRoomManager.close(rid); } catch (error) { @@ -184,7 +184,7 @@ const RoomMenu = ({ } LegacyRoomManager.close(subscription.t + subscription.name); - router.push({}); + router.navigate('/home'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } diff --git a/apps/meteor/client/sidebar/header/actions/Directory.tsx b/apps/meteor/client/sidebar/header/actions/Directory.tsx index 76ae51042ac6..cdfdcfc1eaf1 100644 --- a/apps/meteor/client/sidebar/header/actions/Directory.tsx +++ b/apps/meteor/client/sidebar/header/actions/Directory.tsx @@ -1,15 +1,17 @@ import { Sidebar } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useLayout, useRoute } from '@rocket.chat/ui-contexts'; -import type { HTMLAttributes, VFC } from 'react'; +import { useLayout, useRouter } from '@rocket.chat/ui-contexts'; +import type { HTMLAttributes } from 'react'; import React from 'react'; -const Directory: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { - const directoryRoute = useRoute('directory'); +type DirectoryProps = Omit<HTMLAttributes<HTMLElement>, 'is'>; + +const Directory = (props: DirectoryProps) => { + const router = useRouter(); const { sidebar } = useLayout(); const handleDirectory = useMutableCallback(() => { sidebar.toggle(); - directoryRoute.push({}); + router.navigate('/directory'); }); return <Sidebar.TopBar.Action {...props} icon='notebook-hashtag' onClick={handleDirectory} />; diff --git a/apps/meteor/client/sidebar/header/actions/Home.tsx b/apps/meteor/client/sidebar/header/actions/Home.tsx index 9450bd4c12ab..2c4bbec40f51 100644 --- a/apps/meteor/client/sidebar/header/actions/Home.tsx +++ b/apps/meteor/client/sidebar/header/actions/Home.tsx @@ -1,16 +1,16 @@ import { Sidebar } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useLayout, useRoute, useSetting } from '@rocket.chat/ui-contexts'; +import { useRouter, useLayout, useSetting } from '@rocket.chat/ui-contexts'; import type { HTMLAttributes, VFC } from 'react'; import React from 'react'; const SidebarHeaderActionHome: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { - const homeRoute = useRoute('home'); + const router = useRouter(); const { sidebar } = useLayout(); const showHome = useSetting('Layout_Show_Home_Button'); const handleHome = useMutableCallback(() => { sidebar.toggle(); - homeRoute.push({}); + router.navigate('/home'); }); return showHome ? <Sidebar.TopBar.Action {...props} icon='home' onClick={handleHome} /> : null; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index 3c8dada44e51..97bcb291a37e 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -1,6 +1,5 @@ -import { useTranslation, useRoute, useMethod, useSetModal, useRole } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRoute, useMethod, useSetModal, useRole, useRouter } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import React from 'react'; import type { AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; @@ -16,6 +15,7 @@ type useAdministrationItemProps = { showWorkspace: boolean; }; export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAdministrationItemProps): GenericMenuItemProps[] => { + const router = useRouter(); const t = useTranslation(); const { tabType, trialEndDate, isLoading } = useUpgradeTabParams(); @@ -74,7 +74,7 @@ export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAd const accountBoxItem: GenericMenuItemProps[] = accountBoxItems.map((item, key) => { const action = () => { if (item.href) { - FlowRouter.go(item.href); + router.navigate(item.href); } }; return { diff --git a/apps/meteor/client/startup/e2e.ts b/apps/meteor/client/startup/e2e.ts index c58e2ad3caf0..5b3f84c189f9 100644 --- a/apps/meteor/client/startup/e2e.ts +++ b/apps/meteor/client/startup/e2e.ts @@ -1,5 +1,4 @@ import type { AtLeast, IMessage, ISubscription } from '@rocket.chat/core-typings'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; @@ -11,6 +10,7 @@ import { onClientBeforeSendMessage } from '../lib/onClientBeforeSendMessage'; import { onClientMessageReceived } from '../lib/onClientMessageReceived'; import { isLayoutEmbedded } from '../lib/utils/isLayoutEmbedded'; import { waitUntilFind } from '../lib/utils/waitUntilFind'; +import { router } from '../providers/RouterProvider'; Meteor.startup(() => { Tracker.autorun(() => { @@ -18,7 +18,7 @@ Meteor.startup(() => { return; } - const adminEmbedded = isLayoutEmbedded() && FlowRouter.current().path.startsWith('/admin'); + const adminEmbedded = isLayoutEmbedded() && router.getLocationPathname().startsWith('/admin'); if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) { e2e.startClient(); diff --git a/apps/meteor/client/startup/iframeCommands.ts b/apps/meteor/client/startup/iframeCommands.ts index 8cfc1a9a24a4..3ecf578587bb 100644 --- a/apps/meteor/client/startup/iframeCommands.ts +++ b/apps/meteor/client/startup/iframeCommands.ts @@ -1,6 +1,6 @@ import type { UserStatus, IUser } from '@rocket.chat/core-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { FlowRouter } from 'meteor/kadira:flow-router'; +import type { LocationPathname } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; @@ -10,6 +10,7 @@ import { sdk } from '../../app/utils/client/lib/SDKClient'; import { callbacks } from '../../lib/callbacks'; import { capitalize, ltrim, rtrim } from '../../lib/utils/stringUtils'; import { baseURI } from '../lib/baseURI'; +import { router } from '../providers/RouterProvider'; import { add, remove } from '../views/room/lib/Toolbox/IframeButtons'; const commands = { @@ -24,8 +25,14 @@ const commands = { return ret; }, {} as Record<string, string>); - const newPath = newUrl.pathname.replace(new RegExp(`^${escapeRegExp(__meteor_runtime_config__.ROOT_URL_PATH_PREFIX)}`), ''); - FlowRouter.go(newPath, undefined, { ...FlowRouter.current().queryParams, ...newParams }); + const newPath = newUrl.pathname.replace( + new RegExp(`^${escapeRegExp(__meteor_runtime_config__.ROOT_URL_PATH_PREFIX)}`), + '', + ) as LocationPathname; + router.navigate({ + pathname: newPath, + search: { ...router.getSearchParameters(), ...newParams }, + }); }, 'set-user-status'(data: { status: UserStatus }) { @@ -75,7 +82,7 @@ const commands = { } void callbacks.run('afterLogoutCleanUp', user); sdk.call('logoutCleanUp', user as unknown as IUser); - return FlowRouter.go('home'); + return router.navigate('/home'); }); }, diff --git a/apps/meteor/client/startup/loginViaQuery.ts b/apps/meteor/client/startup/loginViaQuery.ts index 5c7fcbf8e7d5..f22fb80d1c71 100644 --- a/apps/meteor/client/startup/loginViaQuery.ts +++ b/apps/meteor/client/startup/loginViaQuery.ts @@ -1,20 +1,31 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; +import { router } from '../providers/RouterProvider'; + Meteor.startup(() => { Tracker.afterFlush(() => { - const resumeToken = FlowRouter.getQueryParam('resumeToken'); + const { resumeToken } = router.getSearchParameters(); if (!resumeToken) { return; } Meteor.loginWithToken(resumeToken, () => { - if (FlowRouter.getRouteName()) { - FlowRouter.setQueryParams({ resumeToken: null, userId: null }); - return; + const routeName = router.getRouteName(); + + if (!routeName) { + router.navigate('/home'); } - FlowRouter.go('/home'); + + const { resumeToken: _, userId: __, ...search } = router.getSearchParameters(); + + router.navigate( + { + pathname: router.getLocationPathname(), + search, + }, + { replace: true }, + ); }); }); }); diff --git a/apps/meteor/client/startup/notifications/konchatNotifications.ts b/apps/meteor/client/startup/notifications/konchatNotifications.ts index 8729ee3da271..80f60ab68ff2 100644 --- a/apps/meteor/client/startup/notifications/konchatNotifications.ts +++ b/apps/meteor/client/startup/notifications/konchatNotifications.ts @@ -1,5 +1,4 @@ import type { AtLeast, ISubscription, IUser, ICalendarNotification } from '@rocket.chat/core-typings'; -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { lazy } from 'react'; @@ -14,6 +13,7 @@ import { RoomManager } from '../../lib/RoomManager'; import { imperativeModal } from '../../lib/imperativeModal'; import { fireGlobalEvent } from '../../lib/utils/fireGlobalEvent'; import { isLayoutEmbedded } from '../../lib/utils/isLayoutEmbedded'; +import { router } from '../../providers/RouterProvider'; const OutlookCalendarEventModal = lazy(() => import('../../views/outlookCalendar/OutlookCalendarEventModal')); @@ -23,7 +23,7 @@ const notifyNewRoom = async (sub: AtLeast<ISubscription, 'rid'>): Promise<void> return; } - if ((!FlowRouter.getParam('name') || FlowRouter.getParam('name') !== sub.name) && !sub.ls && sub.alert === true) { + if ((!router.getRouteParameters().name || router.getRouteParameters().name !== sub.name) && !sub.ls && sub.alert === true) { KonchatNotification.newRoom(sub.rid); } }; @@ -83,7 +83,7 @@ Meteor.startup(() => { return; } Notifications.onUser('notification', (notification) => { - const openedRoomId = ['channel', 'group', 'direct'].includes(FlowRouter.getRouteName()) ? RoomManager.opened : undefined; + const openedRoomId = ['channel', 'group', 'direct'].includes(router.getRouteName()!) ? RoomManager.opened : undefined; // This logic is duplicated in /client/startup/unread.coffee. const hasFocus = readMessage.isEnable(); diff --git a/apps/meteor/client/startup/reloadRoomAfterLogin.ts b/apps/meteor/client/startup/reloadRoomAfterLogin.ts index 99f1e88f804b..a2788616d732 100644 --- a/apps/meteor/client/startup/reloadRoomAfterLogin.ts +++ b/apps/meteor/client/startup/reloadRoomAfterLogin.ts @@ -1,9 +1,9 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { LegacyRoomManager } from '../../app/ui-utils/client'; import { roomCoordinator } from '../lib/rooms/roomCoordinator'; +import { router } from '../providers/RouterProvider'; Meteor.startup(() => { // Reload rooms after login @@ -14,13 +14,14 @@ Meteor.startup(() => { currentUsername = user?.username; LegacyRoomManager.closeAllRooms(); // Reload only if the current route is a channel route - const routeName = FlowRouter.current().route?.name; + const routeName = router.getRouteName(); if (!routeName) { return; } const roomType = roomCoordinator.getRouteNameIdentifier(routeName); if (roomType) { - FlowRouter.reload(); + router; // TODO: fix this + // router.navigate(0); } } }); diff --git a/apps/meteor/client/startup/routes.tsx b/apps/meteor/client/startup/routes.tsx index 78363d1d91cf..d6139c93cd78 100644 --- a/apps/meteor/client/startup/routes.tsx +++ b/apps/meteor/client/startup/routes.tsx @@ -1,271 +1,233 @@ -import type { IUser } from '@rocket.chat/core-typings'; -import { Accounts } from 'meteor/accounts-base'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Meteor } from 'meteor/meteor'; -import { Tracker } from 'meteor/tracker'; -import React, { lazy } from 'react'; +import React, { createElement, lazy, useEffect } from 'react'; -import { KonchatNotification } from '../../app/ui/client/lib/KonchatNotification'; -import { sdk } from '../../app/utils/client/lib/SDKClient'; -import { t } from '../../app/utils/lib/i18n'; import { appLayout } from '../lib/appLayout'; -import { dispatchToastMessage } from '../lib/toast'; +import { router } from '../providers/RouterProvider'; import MainLayout from '../views/root/MainLayout'; -const PageLoading = lazy(() => import('../views/root/PageLoading')); +const IndexRoute = lazy(() => import('../views/root/IndexRoute')); +const MeetRoute = lazy(() => import('../views/meet/MeetRoute')); const HomePage = lazy(() => import('../views/home/HomePage')); +const DirectoryPage = lazy(() => import('../views/directory')); +const OmnichannelDirectoryPage = lazy(() => import('../views/omnichannel/directory/OmnichannelDirectoryPage')); +const OmnichannelQueueList = lazy(() => import('../views/omnichannel/queueList')); +const CMSPage = lazy(() => import('@rocket.chat/web-ui-registration').then(({ CMSPage }) => ({ default: CMSPage }))); +const SecretURLPage = lazy(() => import('../views/invite/SecretURLPage')); const InvitePage = lazy(() => import('../views/invite/InvitePage')); const ConferenceRoute = lazy(() => import('../views/conference/ConferenceRoute')); - -const SecretURLPage = lazy(() => import('../views/invite/SecretURLPage')); -const CMSPage = lazy(() => import('@rocket.chat/web-ui-registration').then(({ CMSPage }) => ({ default: CMSPage }))); +const SetupWizardRoute = lazy(() => import('../views/setupWizard/SetupWizardRoute')); +const MailerUnsubscriptionPage = lazy(() => import('../views/mailer/MailerUnsubscriptionPage')); +const LoginTokenRoute = lazy(() => import('../views/root/LoginTokenRoute')); const ResetPasswordPage = lazy(() => import('@rocket.chat/web-ui-registration').then(({ ResetPasswordPage }) => ({ default: ResetPasswordPage })), ); - -const MailerUnsubscriptionPage = lazy(() => import('../views/mailer/MailerUnsubscriptionPage')); -const SetupWizardRoute = lazy(() => import('../views/setupWizard/SetupWizardRoute')); -const NotFoundPage = lazy(() => import('../views/notFound/NotFoundPage')); -const MeetPage = lazy(() => import('../views/meet/MeetPage')); - -const DirectoryPage = lazy(() => import('../views/directory')); -const OmnichannelDirectoryPage = lazy(() => import('../views/omnichannel/directory/OmnichannelDirectoryPage')); -const OmnichannelQueueList = lazy(() => import('../views/omnichannel/queueList')); - const OAuthAuthorizationPage = lazy(() => import('../views/oauth/OAuthAuthorizationPage')); const OAuthErrorPage = lazy(() => import('../views/oauth/OAuthErrorPage')); +const NotFoundPage = lazy(() => import('../views/notFound/NotFoundPage')); -FlowRouter.wait(); - -FlowRouter.route('/', { - name: 'index', - action() { - appLayout.render( - <MainLayout> - <PageLoading /> - </MainLayout>, - ); - - if (!Meteor.userId()) { - return FlowRouter.go('home'); - } - - Tracker.autorun((c) => { - if (FlowRouter.subsReady() === true) { - setTimeout(async () => { - const user = Meteor.user() as IUser | null; - if (user?.defaultRoom) { - const room = user.defaultRoom.split('/'); - FlowRouter.go(room[0], { name: room[1] }, FlowRouter.current().queryParams); - } else { - FlowRouter.go('home'); - } - }, 0); - c.stop(); - } - }); - }, -}); - -FlowRouter.route('/login', { - name: 'login', - - action() { - FlowRouter.go('home'); - }, -}); - -FlowRouter.route('/meet/:rid', { - name: 'meet', - - async action(_params, queryParams) { - if (queryParams?.token !== undefined) { - // visitor login - const result = await sdk.rest.get(`/v1/livechat/visitor/${queryParams.token}`); - if ('visitor' in result) { - appLayout.render(<MeetPage />); - return; - } - - dispatchToastMessage({ type: 'error', message: t('Visitor_does_not_exist') }); - return; - } - - if (!Meteor.userId()) { - FlowRouter.go('home'); - return; - } - - appLayout.render(<MeetPage />); - }, -}); - -FlowRouter.route('/home', { - name: 'home', - - action(_params, queryParams) { - KonchatNotification.getDesktopPermission(); - if (queryParams?.saml_idp_credentialToken !== undefined) { - const token = queryParams.saml_idp_credentialToken; - FlowRouter.setQueryParams({ - saml_idp_credentialToken: null, - }); - (Meteor as any).loginWithSamlToken(token, (error?: unknown) => { - if (error) { - dispatchToastMessage({ type: 'error', message: error }); - } - - appLayout.render( - <MainLayout> - <HomePage /> - </MainLayout>, - ); - }); - - return; - } - - appLayout.render( +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'index': { + pathname: '/'; + pattern: '/'; + }; + 'login': { + pathname: '/login'; + pattern: '/login'; + }; + 'meet': { + pathname: `/meet/${string}`; + pattern: '/meet/:rid'; + }; + 'home': { + pathname: '/home'; + pattern: '/home'; + }; + 'directory': { + pathname: `/directory${`/${'users' | 'channels' | 'teams' | 'external'}` | ''}`; + pattern: '/directory/:tab?'; + }; + 'omnichannel-directory': { + pathname: `/omnichannel-directory${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}${ + | `/${string}` + | ''}`; + pattern: '/omnichannel-directory/:page?/:bar?/:id?/:tab?/:context?'; + }; + 'livechat-queue': { + pathname: '/livechat-queue'; + pattern: '/livechat-queue'; + }; + 'terms-of-service': { + pathname: '/terms-of-service'; + pattern: '/terms-of-service'; + }; + 'privacy-policy': { + pathname: '/privacy-policy'; + pattern: '/privacy-policy'; + }; + 'legal-notice': { + pathname: '/legal-notice'; + pattern: '/legal-notice'; + }; + 'register-secret-url': { + pathname: `/register/${string}`; + pattern: '/register/:hash'; + }; + 'invite': { + pathname: `/invite/${string}`; + pattern: '/invite/:hash'; + }; + 'conference': { + pathname: `/conference/${string}`; + pattern: '/conference/:id'; + }; + 'setup-wizard': { + pathname: `/setup-wizard${`/${string}` | ''}`; + pattern: '/setup-wizard/:step?'; + }; + 'mailer-unsubscribe': { + pathname: `/mailer/unsubscribe/${string}/${string}`; + pattern: '/mailer/unsubscribe/:_id/:createdAt'; + }; + 'tokenLogin': { + pathname: `/login-token/${string}`; + pattern: '/login-token/:token'; + }; + 'resetPassword': { + pathname: `/reset-password/${string}`; + pattern: '/reset-password/:token'; + }; + 'oauth/authorize': { + pathname: `/oauth/authorize`; + pattern: '/oauth/authorize'; + }; + 'oauth/error': { + pathname: `/oauth/error/${string}`; + pattern: '/oauth/error/:error'; + }; + } +} + +router.defineRoutes([ + { + path: '/', + id: 'index', + element: appLayout.wrap(<IndexRoute />), + }, + { + path: '/login', + id: 'login', + element: createElement(() => { + useEffect(() => { + router.navigate('/home'); + }, []); + + return null; + }), + }, + { + path: '/meet/:rid', + id: 'meet', + element: appLayout.wrap(<MeetRoute />), + }, + { + path: '/home', + id: 'home', + element: appLayout.wrap( <MainLayout> <HomePage /> </MainLayout>, - ); + ), }, -}); - -FlowRouter.route('/directory/:tab?', { - name: 'directory', - action: () => { - appLayout.render( + { + path: '/directory/:tab?', + id: 'directory', + element: appLayout.wrap( <MainLayout> <DirectoryPage /> </MainLayout>, - ); + ), }, -}); - -FlowRouter.route('/omnichannel-directory/:page?/:bar?/:id?/:tab?/:context?', { - name: 'omnichannel-directory', - action: () => { - appLayout.render( + { + path: '/omnichannel-directory/:page?/:bar?/:id?/:tab?/:context?', + id: 'omnichannel-directory', + element: appLayout.wrap( <MainLayout> <OmnichannelDirectoryPage /> </MainLayout>, - ); + ), }, -}); - -FlowRouter.route('/livechat-queue', { - name: 'livechat-queue', - action: () => { - appLayout.render( + { + path: '/livechat-queue', + id: 'livechat-queue', + element: appLayout.wrap( <MainLayout> <OmnichannelQueueList /> </MainLayout>, - ); - }, -}); - -FlowRouter.route('/terms-of-service', { - name: 'terms-of-service', - action: () => { - appLayout.render(<CMSPage page='Layout_Terms_of_Service' />); - }, -}); - -FlowRouter.route('/privacy-policy', { - name: 'privacy-policy', - action: () => { - appLayout.render(<CMSPage page='Layout_Privacy_Policy' />); - }, -}); - -FlowRouter.route('/legal-notice', { - name: 'legal-notice', - action: () => { - appLayout.render(<CMSPage page='Layout_Legal_Notice' />); - }, -}); - -FlowRouter.route('/register/:hash', { - name: 'register-secret-url', - action: () => { - appLayout.render(<SecretURLPage />); - }, -}); - -FlowRouter.route('/invite/:hash', { - name: 'invite', - action: () => { - appLayout.render(<InvitePage />); - }, -}); - -FlowRouter.route('/conference/:id', { - name: 'conference', - action: () => { - appLayout.render(<ConferenceRoute />); - }, -}); - -FlowRouter.route('/setup-wizard/:step?', { - name: 'setup-wizard', - action: () => { - appLayout.renderStandalone(<SetupWizardRoute />); - }, -}); - -FlowRouter.route('/mailer/unsubscribe/:_id/:createdAt', { - name: 'mailer-unsubscribe', - action: () => { - appLayout.render(<MailerUnsubscriptionPage />); - }, -}); - -FlowRouter.route('/login-token/:token', { - name: 'tokenLogin', - action(params) { - Accounts.callLoginMethod({ - methodArguments: [ - { - loginToken: params?.token, - }, - ], - userCallback(error) { - console.error(error); - FlowRouter.go('/'); - }, - }); - }, -}); - -FlowRouter.route('/reset-password/:token', { - name: 'resetPassword', - action() { - appLayout.render(<ResetPasswordPage />); - }, -}); - -FlowRouter.route('/oauth/authorize', { - name: 'oauth/authorize', - action() { - appLayout.render(<OAuthAuthorizationPage />); - }, -}); - -FlowRouter.route('/oauth/error/:error', { - name: 'oauth/error', - action() { - appLayout.render(<OAuthErrorPage />); - }, -}); - -FlowRouter.notFound = { - action: (): void => { - appLayout.render(<NotFoundPage />); - }, -}; - -Meteor.startup(() => { - FlowRouter.initialize(); -}); + ), + }, + { + path: '/terms-of-service', + id: 'terms-of-service', + element: appLayout.wrap(<CMSPage page='Layout_Terms_of_Service' />), + }, + { + path: '/privacy-policy', + id: 'privacy-policy', + element: appLayout.wrap(<CMSPage page='Layout_Privacy_Policy' />), + }, + { + path: '/legal-notice', + id: 'legal-notice', + element: appLayout.wrap(<CMSPage page='Layout_Legal_Notice' />), + }, + { + path: '/register/:hash', + id: 'register-secret-url', + element: appLayout.wrap(<SecretURLPage />), + }, + { + path: '/invite/:hash', + id: 'invite', + element: appLayout.wrap(<InvitePage />), + }, + { + path: '/conference/:id', + id: 'conference', + element: appLayout.wrap(<ConferenceRoute />), + }, + { + path: '/setup-wizard/:step?', + id: 'setup-wizard', + element: <SetupWizardRoute />, + }, + { + path: '/mailer/unsubscribe/:_id/:createdAt', + id: 'mailer-unsubscribe', + element: appLayout.wrap(<MailerUnsubscriptionPage />), + }, + { + path: '/login-token/:token', + id: 'tokenLogin', + element: appLayout.wrap(<LoginTokenRoute />), + }, + { + path: '/reset-password/:token', + id: 'resetPassword', + element: appLayout.wrap(<ResetPasswordPage />), + }, + { + path: '/oauth/authorize', + id: 'oauth/authorize', + element: appLayout.wrap(<OAuthAuthorizationPage />), + }, + { + path: '/oauth/error/:error', + id: 'oauth/error', + element: appLayout.wrap(<OAuthErrorPage />), + }, + { + path: '*', + id: 'not-found', + element: <NotFoundPage />, + }, +]); diff --git a/apps/meteor/client/startup/setupWizard.ts b/apps/meteor/client/startup/setupWizard.ts index 8c67674d1b02..463b1629fd35 100644 --- a/apps/meteor/client/startup/setupWizard.ts +++ b/apps/meteor/client/startup/setupWizard.ts @@ -1,9 +1,9 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { hasRole } from '../../app/authorization/client'; import { settings } from '../../app/settings/client'; +import { router } from '../providers/RouterProvider'; Meteor.startup(() => { Tracker.autorun(() => { @@ -14,7 +14,7 @@ Meteor.startup(() => { const mustRedirect = (!userId && setupWizardState === 'pending') || isWizardInProgress; if (mustRedirect) { - FlowRouter.go('setup-wizard'); + router.navigate('/setup-wizard'); } }); }); diff --git a/apps/meteor/client/stories/contexts/RouterContextMock.tsx b/apps/meteor/client/stories/contexts/RouterContextMock.tsx index 1d97ddc73eb1..9c0a9183d589 100644 --- a/apps/meteor/client/stories/contexts/RouterContextMock.tsx +++ b/apps/meteor/client/stories/contexts/RouterContextMock.tsx @@ -15,8 +15,8 @@ const RouterContextMock = ({ children }: RouterContextMockProps): ReactElement = const value = useMemo( (): ContextType<typeof RouterContext> => ({ ...parent, - pushRoute: (name, parameters, queryStringParameters): void => { - logAction('pushRoute', name, parameters, queryStringParameters); + navigate: (...args): void => { + logAction('navigate', ...args); }, }), [parent], diff --git a/apps/meteor/client/views/account/AccountRouter.tsx b/apps/meteor/client/views/account/AccountRouter.tsx index 0934aa974b25..8196a877f98c 100644 --- a/apps/meteor/client/views/account/AccountRouter.tsx +++ b/apps/meteor/client/views/account/AccountRouter.tsx @@ -1,4 +1,4 @@ -import { useCurrentRoute, useRoute } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement, ReactNode } from 'react'; import React, { Suspense, useEffect } from 'react'; @@ -11,16 +11,19 @@ type AccountRouterProps = { }; const AccountRouter = ({ children }: AccountRouterProps): ReactElement => { - const [routeName] = useCurrentRoute(); - const defaultRoute = useRoute('profile'); + const router = useRouter(); - useEffect(() => { - if (routeName !== 'account-index') { - return; - } + useEffect( + () => + router.subscribeToRouteChange(() => { + if (router.getRouteName() !== 'account-index') { + return; + } - defaultRoute.replace(); - }, [routeName, defaultRoute]); + router.navigate('/account/profile', { replace: true }); + }), + [router], + ); return children ? ( <> diff --git a/apps/meteor/client/views/account/AccountSidebar.tsx b/apps/meteor/client/views/account/AccountSidebar.tsx index 3e5971227bde..e9e1b5e9c5cf 100644 --- a/apps/meteor/client/views/account/AccountSidebar.tsx +++ b/apps/meteor/client/views/account/AccountSidebar.tsx @@ -1,4 +1,4 @@ -import { useRoutePath, useCurrentRoute, useTranslation, useLayout } from '@rocket.chat/ui-contexts'; +import { useCurrentRoutePath, useTranslation, useLayout } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { memo } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; @@ -14,9 +14,7 @@ const AccountSidebar: FC = () => { const { sidebar } = useLayout(); - const currentRoute = useCurrentRoute(); - const [currentRouteName, currentRouteParams, currentQueryStringParams] = currentRoute; - const currentPath = useRoutePath(currentRouteName || '', currentRouteParams, currentQueryStringParams); + const currentPath = useCurrentRoutePath(); // TODO: uplift this provider return ( diff --git a/apps/meteor/client/views/account/routes.tsx b/apps/meteor/client/views/account/routes.tsx index 17bfc1525283..d31287fa7a57 100644 --- a/apps/meteor/client/views/account/routes.tsx +++ b/apps/meteor/client/views/account/routes.tsx @@ -2,6 +2,39 @@ import { lazy } from 'react'; import { createRouteGroup } from '../../lib/createRouteGroup'; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'account-index': { + pathname: '/account'; + pattern: '/account'; + }; + 'preferences': { + pathname: '/account/preferences'; + pattern: '/account/preferences'; + }; + 'profile': { + pathname: '/account/profile'; + pattern: '/account/profile'; + }; + 'security': { + pathname: '/account/security'; + pattern: '/account/security'; + }; + 'integrations': { + pathname: '/account/integrations'; + pattern: '/account/integrations'; + }; + 'tokens': { + pathname: '/account/tokens'; + pattern: '/account/tokens'; + }; + 'omnichannel': { + pathname: '/account/omnichannel'; + pattern: '/account/omnichannel'; + }; + } +} + export const registerAccountRoute = createRouteGroup( 'account', '/account', diff --git a/apps/meteor/client/views/account/sidebarItems.ts b/apps/meteor/client/views/account/sidebarItems.ts index 3f36d5a71361..c2810bf48564 100644 --- a/apps/meteor/client/views/account/sidebarItems.ts +++ b/apps/meteor/client/views/account/sidebarItems.ts @@ -9,36 +9,36 @@ export const { subscribeToSidebarItems: subscribeToAccountSidebarItems, } = createSidebarItems([ { - href: 'preferences', + href: '/account/preferences', i18nLabel: 'Preferences', icon: 'customize', }, { - href: 'profile', + href: '/account/profile', i18nLabel: 'Profile', icon: 'user', permissionGranted: (): boolean => settings.get('Accounts_AllowUserProfileChange'), }, { - href: 'security', + href: '/account/security', i18nLabel: 'Security', icon: 'lock', permissionGranted: (): boolean => settings.get('Accounts_TwoFactorAuthentication_Enabled') || settings.get('E2E_Enable'), }, { - href: 'integrations', + href: '/account/integrations', i18nLabel: 'Integrations', icon: 'code', permissionGranted: (): boolean => settings.get('Webdav_Integration_Enabled'), }, { - href: 'tokens', + href: '/account/tokens', i18nLabel: 'Personal_Access_Tokens', icon: 'key', permissionGranted: (): boolean => hasPermission('create-personal-access-tokens'), }, { - href: 'omnichannel', + href: '/account/omnichannel', i18nLabel: 'Omnichannel', icon: 'headset', permissionGranted: (): boolean => hasAtLeastOnePermission(['send-omnichannel-chat-transcript', 'request-pdf-transcript']), diff --git a/apps/meteor/client/views/admin/AdministrationRouter.tsx b/apps/meteor/client/views/admin/AdministrationRouter.tsx index 498585bfaaa5..93ea650d0d33 100644 --- a/apps/meteor/client/views/admin/AdministrationRouter.tsx +++ b/apps/meteor/client/views/admin/AdministrationRouter.tsx @@ -1,37 +1,65 @@ -import { useCurrentRoute, useRoute } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement, ReactNode } from 'react'; import React, { Suspense, useEffect } from 'react'; import PageSkeleton from '../../components/PageSkeleton'; -import { useDefaultRoute } from '../../hooks/useDefaultRoute'; +import type { Item, SidebarDivider, SidebarItem } from '../../lib/createSidebarItems'; +import { isGoRocketChatLink } from '../../lib/createSidebarItems'; import SettingsProvider from '../../providers/SettingsProvider'; import { useUpgradeTabParams } from '../hooks/useUpgradeTabParams'; import AdministrationLayout from './AdministrationLayout'; import { getAdminSidebarItems } from './sidebarItems'; +const isSidebarDivider = (sidebarItem: SidebarItem): sidebarItem is SidebarDivider => { + return (sidebarItem as SidebarDivider).divider === true; +}; + +const firstSidebarPage = (sidebarItem: SidebarItem): sidebarItem is Item => { + if (isSidebarDivider(sidebarItem)) { + return false; + } + + return Boolean(sidebarItem.permissionGranted?.()); +}; + type AdministrationRouterProps = { children?: ReactNode; }; const AdministrationRouter = ({ children }: AdministrationRouterProps): ReactElement => { + const router = useRouter(); const { tabType, trialEndDate, isLoading } = useUpgradeTabParams(); - const [routeName] = useCurrentRoute(); - const defaultRoute = useDefaultRoute(getAdminSidebarItems, 'admin-info'); - const upgradeRoute = useRoute('upgrade'); + useEffect( + () => + router.subscribeToRouteChange(() => { + if (router.getRouteName() !== 'admin-index') { + return; + } + + if (tabType && !isLoading) { + router.navigate( + { + name: 'upgrade', + params: { type: tabType }, + search: trialEndDate ? { trialEndDate } : undefined, + }, + { replace: true }, + ); + return; + } - useEffect(() => { - if (routeName !== 'admin-index') { - return; - } + const defaultRoutePath = getAdminSidebarItems().find(firstSidebarPage)?.href ?? '/admin/info'; - if (tabType && !isLoading) { - upgradeRoute.replace({ type: tabType }, trialEndDate ? { trialEndDate } : undefined); - return; - } + if (isGoRocketChatLink(defaultRoutePath)) { + window.open(defaultRoutePath, '_blank'); + return; + } - defaultRoute.replace(); - }, [upgradeRoute, routeName, tabType, trialEndDate, defaultRoute, isLoading]); + router.navigate(defaultRoutePath, { replace: true }); + }), + [tabType, trialEndDate, isLoading, router], + ); return ( <AdministrationLayout> diff --git a/apps/meteor/client/views/admin/import/ImportHistoryPage.tsx b/apps/meteor/client/views/admin/import/ImportHistoryPage.tsx index b1d514a076c7..8c4af4696ee5 100644 --- a/apps/meteor/client/views/admin/import/ImportHistoryPage.tsx +++ b/apps/meteor/client/views/admin/import/ImportHistoryPage.tsx @@ -1,6 +1,6 @@ import { Button, ButtonGroup, Table, TableHead, TableCell, TableRow, TableBody } from '@rocket.chat/fuselage'; import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRoute, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useEndpoint, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import React, { useMemo } from 'react'; @@ -19,8 +19,7 @@ function ImportHistoryPage() { const getCurrentImportOperation = useEndpoint('GET', '/v1/getCurrentImportOperation'); const getLatestImportOperations = useEndpoint('GET', '/v1/getLatestImportOperations'); - const newImportRoute = useRoute('admin-import-new'); - const importProgressRoute = useRoute('admin-import-progress'); + const router = useRouter(); const currentOperation = useQuery( ['ImportHistoryPage', 'currentOperation'], @@ -51,7 +50,7 @@ function ImportHistoryPage() { }, [latestOperations.isSuccess, latestOperations.data]); const handleNewImportClick = () => { - newImportRoute.push(); + router.navigate('/admin/import/new'); }; const downloadPendingFilesResult = useMutation({ @@ -69,7 +68,7 @@ function ImportHistoryPage() { } dispatchToastMessage({ type: 'info', message: t('File_Downloads_Started') }); - importProgressRoute.push(); + router.navigate('/admin/import/progress'); }, }); @@ -88,7 +87,7 @@ function ImportHistoryPage() { } dispatchToastMessage({ type: 'info', message: t('File_Downloads_Started') }); - importProgressRoute.push(); + router.navigate('/admin/import/progress'); }, }); diff --git a/apps/meteor/client/views/admin/import/ImportOperationSummary.js b/apps/meteor/client/views/admin/import/ImportOperationSummary.js index 79e88ddbe358..322f6901f122 100644 --- a/apps/meteor/client/views/admin/import/ImportOperationSummary.js +++ b/apps/meteor/client/views/admin/import/ImportOperationSummary.js @@ -1,5 +1,5 @@ import { TableRow, TableCell } from '@rocket.chat/fuselage'; -import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo } from 'react'; import { @@ -55,17 +55,16 @@ function ImportOperationSummary({ const canCheckProgress = useMemo(() => valid && ImportingStartedStates.includes(status), [valid, status]); - const prepareImportRoute = useRoute('admin-import-prepare'); - const importProgressRoute = useRoute('admin-import-progress'); + const router = useRouter(); const handleClick = () => { if (canContinue) { - prepareImportRoute.push(); + router.navigate('/admin/import/prepare'); return; } if (canCheckProgress) { - importProgressRoute.push(); + router.navigate('/admin/import/progress'); } }; diff --git a/apps/meteor/client/views/admin/import/ImportProgressPage.tsx b/apps/meteor/client/views/admin/import/ImportProgressPage.tsx index 8cd1e78a4b89..d0274ab15111 100644 --- a/apps/meteor/client/views/admin/import/ImportProgressPage.tsx +++ b/apps/meteor/client/views/admin/import/ImportProgressPage.tsx @@ -1,6 +1,6 @@ import { Box, Margins, Throbber } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRoute, useEndpoint, useTranslation, useStream } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useEndpoint, useTranslation, useStream, useRouter } from '@rocket.chat/ui-contexts'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import React, { useEffect } from 'react'; @@ -17,8 +17,7 @@ const ImportProgressPage = function ImportProgressPage() { const dispatchToastMessage = useToastMessageDispatch(); const handleError = useErrorHandler(); - const importHistoryRoute = useRoute('admin-import'); - const prepareImportRoute = useRoute('admin-import-prepare'); + const router = useRouter(); const getCurrentImportOperation = useEndpoint('GET', '/v1/getCurrentImportOperation'); const getImportProgress = useEndpoint('GET', '/v1/getImportProgress'); @@ -43,23 +42,23 @@ const ImportProgressPage = function ImportProgressPage() { refetchInterval: 1000, onSuccess: ({ valid, status }) => { if (!valid) { - importHistoryRoute.push(); + router.navigate('/admin/import'); return; } if (status === 'importer_done') { dispatchToastMessage({ type: 'success', message: t('Importer_done') }); - importHistoryRoute.push(); + router.navigate('/admin/import'); return; } if (!(ImportingStartedStates as string[]).includes(status)) { - prepareImportRoute.push(); + router.navigate('/admin/import/prepare'); } }, onError: (error) => { handleError(error, t('Failed_To_Load_Import_Data')); - importHistoryRoute.push(); + router.navigate('/admin/import'); }, }, ); @@ -82,13 +81,13 @@ const ImportProgressPage = function ImportProgressPage() { type: 'success', message: t(message), }); - importHistoryRoute.push(); + router.navigate('/admin/import'); return; case 'importer_import_failed': case 'importer_import_cancelled': t.has(message) && handleError(message); - importHistoryRoute.push(); + router.navigate('/admin/import'); return; default: @@ -114,7 +113,7 @@ const ImportProgressPage = function ImportProgressPage() { onSuccess: (progress) => { if (!progress) { dispatchToastMessage({ type: 'warning', message: t('Importer_not_in_progress') }); - prepareImportRoute.push(); + router.navigate('/admin/import/prepare'); return; } @@ -130,7 +129,7 @@ const ImportProgressPage = function ImportProgressPage() { }, onError: (error) => { handleError(error, t('Failed_To_Load_Import_Data')); - importHistoryRoute.push(); + router.navigate('/admin/import'); }, }, ); diff --git a/apps/meteor/client/views/admin/import/NewImportPage.js b/apps/meteor/client/views/admin/import/NewImportPage.js index 61e3ecaa8d09..b616579eb920 100644 --- a/apps/meteor/client/views/admin/import/NewImportPage.js +++ b/apps/meteor/client/views/admin/import/NewImportPage.js @@ -14,7 +14,7 @@ import { UrlInput, } from '@rocket.chat/fuselage'; import { useUniqueId, useSafely } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRoute, useRouteParameter, useSetting, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useRouter, useRouteParameter, useSetting, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useState, useMemo, useEffect } from 'react'; import { Importers } from '../../../../app/importer/client/index'; @@ -34,27 +34,31 @@ function NewImportPage() { const maxFileSize = useSetting('FileUpload_MaxFileSize'); - const importHistoryRoute = useRoute('admin-import'); - const newImportRoute = useRoute('admin-import-new'); - const prepareImportRoute = useRoute('admin-import-prepare'); + const router = useRouter(); const uploadImportFile = useEndpoint('POST', '/v1/uploadImportFile'); const downloadPublicImportFile = useEndpoint('POST', '/v1/downloadPublicImportFile'); useEffect(() => { if (importerKey && !importer) { - newImportRoute.replace(); + router.navigate('/admin/import/new', { replace: true }); } - }, [importer, importerKey, newImportRoute]); + }, [importer, importerKey, router]); const formatMemorySize = useFormatMemorySize(); const handleBackToImportsButtonClick = () => { - importHistoryRoute.push(); + router.navigate('/admin/import'); }; const handleImporterKeyChange = (importerKey) => { - newImportRoute.replace({ importerKey }); + router.navigate( + router.buildRoutePath({ + pattern: '/admin/import/new/:importerKey', + params: { importerKey }, + }), + { replace: true }, + ); }; const handleFileTypeChange = (fileType) => { @@ -106,7 +110,7 @@ function NewImportPage() { }), ), ); - prepareImportRoute.push(); + router.navigate('/admin/import/prepare'); } finally { setLoading(false); } @@ -124,7 +128,7 @@ function NewImportPage() { try { await downloadPublicImportFile({ importerKey, fileUrl }); dispatchToastMessage({ type: 'success', message: t('Import_requested_successfully') }); - prepareImportRoute.push(); + router.navigate('/admin/import/prepare'); } catch (error) { handleError(error, t('Failed_To_upload_Import_File')); } finally { @@ -144,7 +148,7 @@ function NewImportPage() { try { await downloadPublicImportFile({ importerKey, fileUrl: filePath }); dispatchToastMessage({ type: 'success', message: t('Import_requested_successfully') }); - prepareImportRoute.push(); + router.navigate('/admin/import/prepare'); } catch (error) { handleError(error, t('Failed_To_upload_Import_File')); } finally { diff --git a/apps/meteor/client/views/admin/import/PrepareImportPage.js b/apps/meteor/client/views/admin/import/PrepareImportPage.js index ebb58b43515e..59e05b5f85d0 100644 --- a/apps/meteor/client/views/admin/import/PrepareImportPage.js +++ b/apps/meteor/client/views/admin/import/PrepareImportPage.js @@ -1,6 +1,6 @@ import { Badge, Box, Button, ButtonGroup, Icon, Margins, Throbber, Tabs } from '@rocket.chat/fuselage'; import { useDebouncedValue, useSafely } from '@rocket.chat/fuselage-hooks'; -import { useRoute, useEndpoint, useTranslation, useStream } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useTranslation, useStream, useRouter } from '@rocket.chat/ui-contexts'; import React, { useEffect, useState, useMemo } from 'react'; import { @@ -48,9 +48,7 @@ function PrepareImportPage() { const usersCount = useMemo(() => users.filter(({ do_import }) => do_import).length, [users]); const channelsCount = useMemo(() => channels.filter(({ do_import }) => do_import).length, [channels]); - const importHistoryRoute = useRoute('admin-import'); - const newImportRoute = useRoute('admin-import-new'); - const importProgressRoute = useRoute('admin-import-progress'); + const router = useRouter(); const getImportFileData = useEndpoint('GET', '/v1/getImportFileData'); const getCurrentImportOperation = useEndpoint('GET', '/v1/getCurrentImportOperation'); @@ -73,13 +71,13 @@ function PrepareImportPage() { if (!data) { handleError(t('Importer_not_setup')); - importHistoryRoute.push(); + router.navigate('/admin/import'); return; } if (data.step) { handleError(t('Failed_To_Load_Import_Data')); - importHistoryRoute.push(); + router.navigate('/admin/import'); return; } @@ -90,7 +88,7 @@ function PrepareImportPage() { setProgressRate(null); } catch (error) { handleError(error, t('Failed_To_Load_Import_Data')); - importHistoryRoute.push(); + router.navigate('/admin/import'); } }; @@ -102,12 +100,12 @@ function PrepareImportPage() { ); if (!operation.valid) { - newImportRoute.push(); + router.navigate('/admin/import/new'); return; } if (ImportingStartedStates.includes(operation.status)) { - importProgressRoute.push(); + router.navigate('/admin/import/progress'); return; } @@ -123,42 +121,28 @@ function PrepareImportPage() { if (ImportingErrorStates.includes(operation.status)) { handleError(t('Import_Operation_Failed')); - importHistoryRoute.push(); + router.navigate('/admin/import'); return; } if (operation.status === ProgressStep.DONE) { - importHistoryRoute.push(); + router.navigate('/admin/import'); return; } handleError(t('Unknown_Import_State')); - importHistoryRoute.push(); + router.navigate('/admin/import'); } catch (error) { handleError(t('Failed_To_Load_Import_Data')); - importHistoryRoute.push(); + router.navigate('/admin/import'); } }; loadCurrentOperation(); - }, [ - getCurrentImportOperation, - getImportFileData, - handleError, - - importHistoryRoute, - importProgressRoute, - newImportRoute, - - setMessageCount, - setPreparing, - setProgressRate, - setStatus, - t, - ]); + }, [getCurrentImportOperation, getImportFileData, handleError, router, setMessageCount, setPreparing, setProgressRate, setStatus, t]); const handleBackToImportsButtonClick = () => { - importHistoryRoute.push(); + router.navigate('/admin/import'); }; const handleStartButtonClick = async () => { @@ -166,10 +150,10 @@ function PrepareImportPage() { try { await startImport({ input: { users, channels } }); - importProgressRoute.push(); + router.navigate('/admin/import/progress'); } catch (error) { handleError(error, t('Failed_To_Start_Import')); - importHistoryRoute.push(); + router.navigate('/admin/import'); } }; diff --git a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/InviteUsers.tsx b/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/InviteUsers.tsx index ab25cf7fd69d..0e35f6e5bac4 100644 --- a/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/InviteUsers.tsx +++ b/apps/meteor/client/views/admin/info/FederationCard/components/FederationModal/InviteUsers.tsx @@ -1,15 +1,14 @@ import { Box, ButtonGroup, Button, Banner } from '@rocket.chat/fuselage'; -import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { FC, ReactElement } from 'react'; import React from 'react'; const InviteUsers: FC<{ onClose: () => void }> = ({ onClose }): ReactElement => { const t = useTranslation(); - - const directoryRoute = useRoute('directory'); + const router = useRouter(); const handleDirectory = (): void => { onClose(); - directoryRoute.push({ tab: 'users' }); + router.navigate('/directory/users'); }; return ( diff --git a/apps/meteor/client/views/admin/moderation/MessageReportInfo.tsx b/apps/meteor/client/views/admin/moderation/MessageReportInfo.tsx index aaeae2d05bee..72b69586efd8 100644 --- a/apps/meteor/client/views/admin/moderation/MessageReportInfo.tsx +++ b/apps/meteor/client/views/admin/moderation/MessageReportInfo.tsx @@ -9,6 +9,7 @@ import UserAvatar from '../../../components/avatar/UserAvatar'; import { useFormatDate } from '../../../hooks/useFormatDate'; import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime'; import { useFormatTime } from '../../../hooks/useFormatTime'; +import { router } from '../../../providers/RouterProvider'; const MessageReportInfo = ({ msgId }: { msgId: string }): JSX.Element => { const t = useTranslation(); @@ -60,7 +61,7 @@ const MessageReportInfo = ({ msgId }: { msgId: string }): JSX.Element => { return ( <> <ContextualbarHeader> - <ContextualbarBack onClick={() => window.history.go(-1)} /> + <ContextualbarBack onClick={() => router.navigate(-1)} /> <ContextualbarTitle>{t('Report')}</ContextualbarTitle> <ContextualbarClose onClick={() => moderationRoute.push({})} /> </ContextualbarHeader> diff --git a/apps/meteor/client/views/admin/routes.tsx b/apps/meteor/client/views/admin/routes.tsx index 8ee81c904292..354604c3bb11 100644 --- a/apps/meteor/client/views/admin/routes.tsx +++ b/apps/meteor/client/views/admin/routes.tsx @@ -1,7 +1,105 @@ import { lazy } from 'react'; +import type { UpgradeTabVariant } from '../../../lib/upgradeTab'; import { createRouteGroup } from '../../lib/createRouteGroup'; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'admin-index': { + pathname: '/admin'; + pattern: '/admin'; + }; + 'custom-sounds': { + pathname: `/admin/custom-sounds${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/custom-sounds/:context?/:id?'; + }; + 'admin-info': { + pathname: '/admin/info'; + pattern: '/admin/info'; + }; + 'admin-import': { + pathname: '/admin/import'; + pattern: '/admin/import'; + }; + 'admin-import-new': { + pathname: `/admin/import/new${`/${string}` | ''}`; + pattern: '/admin/import/new/:importerKey?'; + }; + 'admin-import-prepare': { + pathname: '/admin/import/prepare'; + pattern: '/admin/import/prepare'; + }; + 'admin-import-progress': { + pathname: '/admin/import/progress'; + pattern: '/admin/import/progress'; + }; + 'admin-mailer': { + pathname: '/admin/mailer'; + pattern: '/admin/mailer'; + }; + 'admin-oauth-apps': { + pathname: `/admin/oauth-apps${`/${'new' | 'edit'}` | ''}${`/${string}` | ''}`; + pattern: '/admin/oauth-apps/:context?/:id?'; + }; + 'admin-integrations': { + pathname: `/admin/integrations${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/integrations/:context?/:type?/:id?'; + }; + 'user-status': { + pathname: `/admin/user-status${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/user-status/:context?/:id?'; + }; + 'emoji-custom': { + pathname: `/admin/emoji-custom${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/emoji-custom/:context?/:id?'; + }; + 'admin-users': { + pathname: `/admin/users${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/users/:context?/:id?'; + }; + 'admin-rooms': { + pathname: `/admin/rooms${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/rooms/:context?/:id?'; + }; + 'invites': { + pathname: '/admin/invites'; + pattern: '/admin/invites'; + }; + 'cloud': { + pathname: `/admin/cloud${`/${string}` | ''}`; + pattern: '/admin/cloud/:page?'; + }; + 'admin-view-logs': { + pathname: '/admin/view-logs'; + pattern: '/admin/view-logs'; + }; + 'federation-dashboard': { + pathname: '/admin/federation-dashboard'; + pattern: '/admin/federation-dashboard'; + }; + 'admin-permissions': { + pathname: `/admin/permissions${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/permissions/:context?/:_id?'; + }; + 'admin-email-inboxes': { + pathname: `/admin/email-inboxes${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/email-inboxes/:context?/:_id?'; + }; + 'admin-settings': { + pathname: `/admin/settings${`/${string}` | ''}`; + pattern: '/admin/settings/:group?'; + }; + 'upgrade': { + pathname: `/admin/upgrade${`/${UpgradeTabVariant}` | ''}`; + pattern: '/admin/upgrade/:type?'; + }; + 'moderation-console': { + pathname: `/admin/moderation-console${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/moderation-console/:context?/:id?'; + }; + } +} + export const registerAdminRoute = createRouteGroup( 'admin', '/admin', diff --git a/apps/meteor/client/views/admin/settings/SettingsGroupCard.tsx b/apps/meteor/client/views/admin/settings/SettingsGroupCard.tsx index 4ba1eba67b9d..8bf75c3753d5 100644 --- a/apps/meteor/client/views/admin/settings/SettingsGroupCard.tsx +++ b/apps/meteor/client/views/admin/settings/SettingsGroupCard.tsx @@ -1,10 +1,9 @@ import type { ISetting } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Button, Box } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { Card } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -25,26 +24,27 @@ type SettingsGroupCardProps = { const SettingsGroupCard = ({ id, title, description }: SettingsGroupCardProps): ReactElement => { const t = useTranslation(); - const router = useRoute('admin-settings'); - - const handleOpenGroup = useMutableCallback(() => { - if (id) { - router.push({ - group: id, - }); - } - }); + const router = useRouter(); return ( <Card data-qa-id={id}> <Card.Title>{t(title)}</Card.Title> - <Card.Body height='x88'> + <Card.Body height={88}> <Box className={clampStyle}> {description && t.has(description) && <MarkdownText variant='inlineWithoutBreaks' content={t(description)} />} </Box> </Card.Body> <Card.Footer> - <Button onClick={handleOpenGroup}>{t('Open')}</Button> + <Button + is='a' + href={router.buildRoutePath({ + pattern: '/admin/settings/:group?', + params: { group: id }, + })} + role='button' + > + {t('Open')} + </Button> </Card.Footer> </Card> ); diff --git a/apps/meteor/client/views/admin/sidebar/AdminSidebar.tsx b/apps/meteor/client/views/admin/sidebar/AdminSidebar.tsx index c44338356ceb..b288eaebe02d 100644 --- a/apps/meteor/client/views/admin/sidebar/AdminSidebar.tsx +++ b/apps/meteor/client/views/admin/sidebar/AdminSidebar.tsx @@ -1,4 +1,4 @@ -import { useRoutePath, useCurrentRoute, useTranslation, useLayout } from '@rocket.chat/ui-contexts'; +import { useTranslation, useLayout, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { memo } from 'react'; @@ -12,9 +12,7 @@ const AdminSidebar: FC = () => { const { sidebar } = useLayout(); - const currentRoute = useCurrentRoute(); - const [currentRouteName, currentRouteParams, currentQueryStringParams] = currentRoute; - const currentPath = useRoutePath(currentRouteName || '', currentRouteParams, currentQueryStringParams); + const currentPath = useCurrentRoutePath(); // TODO: uplift this provider return ( diff --git a/apps/meteor/client/views/admin/sidebar/UpgradeTab.tsx b/apps/meteor/client/views/admin/sidebar/UpgradeTab.tsx index f5c67ede0eec..37fa2598390f 100644 --- a/apps/meteor/client/views/admin/sidebar/UpgradeTab.tsx +++ b/apps/meteor/client/views/admin/sidebar/UpgradeTab.tsx @@ -1,7 +1,7 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import { useRoutePath, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React, { useMemo } from 'react'; +import React from 'react'; import type { UpgradeTabVariant } from '../../../../lib/upgradeTab'; import { getUpgradeTabLabel, isFullyFeature } from '../../../../lib/upgradeTab'; @@ -11,23 +11,21 @@ import Sidebar from '../../../components/Sidebar'; type UpgradeTabProps = { type: UpgradeTabVariant; currentPath: string; trialEndDate: string | undefined }; const UpgradeTab = ({ type, currentPath, trialEndDate }: UpgradeTabProps): ReactElement => { - const path = useRoutePath( - 'upgrade', - useMemo( - () => ({ - type, - }), - [type], - ), - useMemo(() => (trialEndDate ? { trialEndDate } : undefined), [trialEndDate]), - ); + const router = useRouter(); + + const path = router.buildRoutePath({ + name: 'upgrade', + params: { type }, + search: trialEndDate ? { trialEndDate } : undefined, + }); + const t = useTranslation(); const label = getUpgradeTabLabel(type); const displayEmoji = isFullyFeature(type); return ( - <Sidebar.GenericItem active={currentPath === path} href={String(path)} featured> + <Sidebar.GenericItem active={currentPath === path} href={path} featured> <Icon name='arrow-stack-up' size='x20' mi='x4' /> <Box withTruncatedText fontScale='p2' mi='x4'> {t(label)} {displayEmoji && <Emoji emojiHandle=':zap:' />} diff --git a/apps/meteor/client/views/admin/sidebarItems.ts b/apps/meteor/client/views/admin/sidebarItems.ts index 3cc27caa1732..5cb699934d13 100644 --- a/apps/meteor/client/views/admin/sidebarItems.ts +++ b/apps/meteor/client/views/admin/sidebarItems.ts @@ -8,82 +8,82 @@ export const { subscribeToSidebarItems: subscribeToAdminSidebarItems, } = createSidebarItems([ { - href: 'admin-info', + href: '/admin/info', i18nLabel: 'Info', icon: 'info-circled', permissionGranted: (): boolean => hasPermission('view-statistics'), }, { icon: 'shield-alt', - href: 'moderation-console', + href: '/admin/moderation-console', i18nLabel: 'Moderation console', tag: 'Beta', permissionGranted: (): boolean => hasPermission('view-moderation-console'), }, { - href: 'admin-import', + href: '/admin/import', i18nLabel: 'Import', icon: 'import', permissionGranted: (): boolean => hasPermission('run-import'), }, { - href: 'admin-users', + href: '/admin/users', i18nLabel: 'Users', icon: 'team', permissionGranted: (): boolean => hasPermission('view-user-administration'), }, { - href: 'admin-rooms', + href: '/admin/rooms', i18nLabel: 'Rooms', icon: 'hashtag', permissionGranted: (): boolean => hasPermission('view-room-administration'), }, { - href: 'invites', + href: '/admin/invites', i18nLabel: 'Invites', icon: 'user-plus', permissionGranted: (): boolean => hasPermission('create-invite-links'), }, { + href: '/admin/cloud', icon: 'cloud-plus', - href: 'cloud', i18nLabel: 'Registration', permissionGranted: (): boolean => hasPermission('manage-cloud'), }, { - href: 'admin-view-logs', + href: '/admin/view-logs', i18nLabel: 'View_Logs', icon: 'post', permissionGranted: (): boolean => hasPermission('view-logs'), }, { - href: 'custom-sounds', + href: '/admin/custom-sounds', i18nLabel: 'Custom_Sounds', icon: 'volume', permissionGranted: (): boolean => hasPermission('manage-sounds'), }, { + href: '/admin/federation-dashboard', icon: 'discover', - href: 'federation-dashboard', i18nLabel: 'Federation Dashboard', permissionGranted: (): boolean => hasPermission('view-federation-data'), }, { + href: '/admin/email-inboxes', icon: 'mail', - href: 'admin-email-inboxes', i18nLabel: 'Email_Inboxes', tag: 'Alpha', permissionGranted: (): boolean => hasPermission('manage-email-inbox'), }, { + href: '/admin/emoji-custom', icon: 'emoji', - href: 'emoji-custom', i18nLabel: 'Custom_Emoji', permissionGranted: (): boolean => hasPermission('manage-emoji'), }, { + href: '/admin/integrations', icon: 'code', - href: 'admin-integrations', i18nLabel: 'Integrations', permissionGranted: (): boolean => hasAtLeastOnePermission([ @@ -94,32 +94,32 @@ export const { ]), }, { + href: '/admin/oauth-apps', icon: 'discover', - href: 'admin-oauth-apps', i18nLabel: 'OAuth Apps', permissionGranted: (): boolean => hasAllPermission('manage-oauth-apps'), }, { + href: '/admin/mailer', icon: 'mail', - href: 'admin-mailer', i18nLabel: 'Mailer', permissionGranted: (): boolean => hasAllPermission('access-mailer'), }, { + href: '/admin/user-status', icon: 'user', - href: 'user-status', i18nLabel: 'User_Status', permissionGranted: (): boolean => hasAtLeastOnePermission(['manage-user-status']), }, { + href: '/admin/permissions', icon: 'lock', - href: 'admin-permissions', i18nLabel: 'Permissions', permissionGranted: (): boolean => hasAtLeastOnePermission(['access-permissions', 'access-setting-permissions']), }, { + href: '/admin/settings', icon: 'customize', - href: 'admin-settings', i18nLabel: 'Settings', permissionGranted: (): boolean => hasAtLeastOnePermission(['view-privileged-setting', 'edit-privileged-setting', 'manage-selected-settings']), diff --git a/apps/meteor/client/views/admin/upgrade/UpgradePage/UpgradePage.tsx b/apps/meteor/client/views/admin/upgrade/UpgradePage/UpgradePage.tsx index e7fbdcd49bbf..57b4f2d3e80e 100644 --- a/apps/meteor/client/views/admin/upgrade/UpgradePage/UpgradePage.tsx +++ b/apps/meteor/client/views/admin/upgrade/UpgradePage/UpgradePage.tsx @@ -1,5 +1,5 @@ import { Throbber, Box } from '@rocket.chat/fuselage'; -import { useLayout, useRouteParameter, useQueryStringParameter, useAbsoluteUrl, useLanguage } from '@rocket.chat/ui-contexts'; +import { useLayout, useRouteParameter, useSearchParameter, useAbsoluteUrl, useLanguage } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect, useRef, useState } from 'react'; @@ -52,7 +52,7 @@ const UpgradePage = (): ReactElement => { const [isLoading, setIsLoading] = useState(true); const type = useRouteParameter('type') as UpgradeTabVariant; - const trialEndDate = useQueryStringParameter('trialEndDate'); + const trialEndDate = useSearchParameter('trialEndDate'); const language = useLanguage(); const pageUrl = getUrl(type, trialEndDate, language); diff --git a/apps/meteor/client/views/conference/ConferencePage.tsx b/apps/meteor/client/views/conference/ConferencePage.tsx index b95106efca39..0b05e8b2b4d2 100644 --- a/apps/meteor/client/views/conference/ConferencePage.tsx +++ b/apps/meteor/client/views/conference/ConferencePage.tsx @@ -17,7 +17,7 @@ const getQueryParams = () => { const ConferencePage = (): ReactElement => { const user = useUser(); - const defaultRoute = useRoute('/'); + const defaultRoute = useRoute('home'); const setModal = useSetModal(); const handleOpenCall = useVideoConfOpenCall(); const userDisplayName = useUserDisplayName({ name: user?.name, username: user?.username }); diff --git a/apps/meteor/client/views/directory/DirectoryPage.tsx b/apps/meteor/client/views/directory/DirectoryPage.tsx index 8024d2ae66db..0015a63babfd 100644 --- a/apps/meteor/client/views/directory/DirectoryPage.tsx +++ b/apps/meteor/client/views/directory/DirectoryPage.tsx @@ -1,5 +1,5 @@ import { Tabs } from '@rocket.chat/fuselage'; -import { useCurrentRoute, useRoute, useRouteParameter, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useRouteParameter, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect, useCallback } from 'react'; @@ -8,26 +8,33 @@ import ChannelsTab from './tabs/channels/ChannelsTab'; import TeamsTab from './tabs/teams/TeamsTab'; import UsersTab from './tabs/users/UsersTab'; +type TabName = 'users' | 'channels' | 'teams' | 'external'; + const DirectoryPage = (): ReactElement => { const t = useTranslation(); - const defaultTab = String(useSetting('Accounts_Directory_DefaultView')); + const defaultTab = useSetting<TabName>('Accounts_Directory_DefaultView') ?? 'users'; const federationEnabled = useSetting('FEDERATION_Enabled'); - const [routeName] = useCurrentRoute(); - const tab = useRouteParameter('tab'); - const directoryRoute = useRoute('directory'); + const tab = useRouteParameter('tab') as TabName | undefined; + const router = useRouter(); + + useEffect( + () => + router.subscribeToRouteChange(() => { + if (router.getRouteName() !== 'directory') { + return; + } - useEffect(() => { - if (routeName !== 'directory') { - return; - } + const { tab } = router.getRouteParameters(); - if (!tab || (tab === 'external' && !federationEnabled)) { - return directoryRoute.replace({ tab: defaultTab }); - } - }, [routeName, directoryRoute, tab, federationEnabled, defaultTab]); + if (!tab || (tab === 'external' && !federationEnabled)) { + router.navigate(`/directory/${defaultTab}`, { replace: true }); + } + }), + [router, federationEnabled, defaultTab], + ); - const handleTabClick = useCallback((tab) => (): void => directoryRoute.push({ tab }), [directoryRoute]); + const handleTabClick = useCallback((tab: TabName) => () => router.navigate(`/directory/${tab}`), [router]); return ( <Page> diff --git a/apps/meteor/client/views/home/HomePage.tsx b/apps/meteor/client/views/home/HomePage.tsx index 667afa9286c5..b9647a0e0ec5 100644 --- a/apps/meteor/client/views/home/HomePage.tsx +++ b/apps/meteor/client/views/home/HomePage.tsx @@ -1,11 +1,41 @@ -import { useSetting } from '@rocket.chat/ui-contexts'; +import { useRouter, useSetting, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; +import { Meteor } from 'meteor/meteor'; import type { ReactElement } from 'react'; -import React from 'react'; +import React, { useEffect } from 'react'; +import { KonchatNotification } from '../../../app/ui/client/lib/KonchatNotification'; import CustomHomePage from './CustomHomePage'; import DefaultHomePage from './DefaultHomePage'; const HomePage = (): ReactElement => { + useEffect(() => { + KonchatNotification.getDesktopPermission(); + }, []); + + const router = useRouter(); + const dispatchToastMessage = useToastMessageDispatch(); + + useEffect(() => { + const { saml_idp_credentialToken: token, ...search } = router.getSearchParameters(); + if (!token) { + return; + } + + Meteor.loginWithSamlToken(token, (error?: unknown) => { + if (error) { + dispatchToastMessage({ type: 'error', message: error }); + } + + router.navigate( + { + pathname: router.getLocationPathname(), + search, + }, + { replace: true }, + ); + }); + }, [dispatchToastMessage, router]); + const customOnly = useSetting('Layout_Custom_Body_Only'); if (customOnly) { diff --git a/apps/meteor/client/views/home/cards/JoinRoomsCard.tsx b/apps/meteor/client/views/home/cards/JoinRoomsCard.tsx index 49543c45f1a8..1a105ea8d58f 100644 --- a/apps/meteor/client/views/home/cards/JoinRoomsCard.tsx +++ b/apps/meteor/client/views/home/cards/JoinRoomsCard.tsx @@ -1,15 +1,15 @@ import { Button } from '@rocket.chat/fuselage'; import { Card } from '@rocket.chat/ui-client'; -import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; const JoinRoomsCard = (): ReactElement => { const t = useTranslation(); - const directoryRoute = useRoute('directory'); + const router = useRouter(); const handleDirectory = (): void => { - directoryRoute.push({}); + router.navigate('/directory'); }; return ( diff --git a/apps/meteor/client/views/invite/InvitePage.tsx b/apps/meteor/client/views/invite/InvitePage.tsx index be2075392e0f..7fd180d85c80 100644 --- a/apps/meteor/client/views/invite/InvitePage.tsx +++ b/apps/meteor/client/views/invite/InvitePage.tsx @@ -24,9 +24,9 @@ const InvitePage = (): ReactElement => { const registrationForm = useSetting('Accounts_RegistrationForm'); const setLoginDefaultState = useSessionDispatch('loginDefaultState'); const userId = useUserId(); - const homeRoute = useRoute('/'); - const groupRoute = useRoute('/group/:name/:tab?/:context?'); - const channelRoute = useRoute('/channel/:name/:tab?/:context?'); + const homeRoute = useRoute('home'); + const groupRoute = useRoute('group'); + const channelRoute = useRoute('channel'); const { isLoading, data } = useQuery( ['invite', token], diff --git a/apps/meteor/client/views/invite/SecretURLPage.tsx b/apps/meteor/client/views/invite/SecretURLPage.tsx index c5f1836c61e7..20cca5787bc9 100644 --- a/apps/meteor/client/views/invite/SecretURLPage.tsx +++ b/apps/meteor/client/views/invite/SecretURLPage.tsx @@ -1,17 +1,17 @@ -import { useUserId, useRoute } from '@rocket.chat/ui-contexts'; +import { useUserId, useRouter } from '@rocket.chat/ui-contexts'; import RegistrationPageRouter from '@rocket.chat/web-ui-registration'; import type { ReactElement } from 'react'; import React, { useEffect } from 'react'; const SecretURLPage = (): ReactElement | null => { const uid = useUserId(); - const homeRouter = useRoute('home'); + const router = useRouter(); useEffect(() => { if (uid) { - homeRouter.replace(); + router.navigate('/home'); } - }, [uid, homeRouter]); + }, [uid, router]); if (uid) { return null; diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx index 55d01c63a0c5..9be183c489dc 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx @@ -2,14 +2,7 @@ import type { ISetting } from '@rocket.chat/apps-engine/definition/settings'; import type { App } from '@rocket.chat/core-typings'; import { Button, ButtonGroup, Box, Throbber } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { - useTranslation, - useCurrentRoute, - useRoute, - useRouteParameter, - useToastMessageDispatch, - usePermission, -} from '@rocket.chat/ui-contexts'; +import { useTranslation, useRouteParameter, useToastMessageDispatch, usePermission, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useState, useCallback, useRef } from 'react'; @@ -31,6 +24,7 @@ import AppSettings from './tabs/AppSettings'; const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); + const router = useRouter(); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const [isSaving, setIsSaving] = useState(false); @@ -38,19 +32,20 @@ const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => { const settingsRef = useRef<Record<string, ISetting['value']>>({}); const isAdminUser = usePermission('manage-apps'); - const [currentRouteName] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - const router = useRoute(currentRouteName); - const tab = useRouteParameter('tab'); const context = useRouteParameter('context'); const appData = useAppInfo(id, context || ''); const handleReturn = useMutableCallback((): void => { - context && router.push({ context, page: 'list' }); + if (!context) { + return; + } + + router.navigate({ + name: 'marketplace', + params: { context, page: 'list' }, + }); }); const { installed, settings, privacyPolicySummary, permissions, tosLink, privacyLink, name } = appData || {}; diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageTabs.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageTabs.tsx index d93af7b44d19..e54613dc5cd8 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageTabs.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPageTabs.tsx @@ -1,5 +1,5 @@ import { Tabs } from '@rocket.chat/fuselage'; -import { useCurrentRoute, usePermission, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { usePermission, useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -17,44 +17,45 @@ const AppDetailsPageTabs = ({ context, installed, isSecurityVisible, settings, t const t = useTranslation(); const isAdminUser = usePermission('manage-apps'); - const [currentRouteName] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - const router = useRoute(currentRouteName); + const router = useRouter(); - const [, urlParams] = useCurrentRoute(); - const handleTabClick = (tab: 'details' | 'security' | 'releases' | 'settings' | 'logs' | 'requests'): void => { - router.replace({ ...urlParams, tab }); + const handleTabClick = (tab: 'details' | 'security' | 'releases' | 'settings' | 'logs' | 'requests') => { + router.navigate( + { + name: 'marketplace', + params: { ...router.getRouteParameters(), tab }, + }, + { replace: true }, + ); }; return ( <Tabs> - <Tabs.Item onClick={(): void => handleTabClick('details')} selected={!tab || tab === 'details'}> + <Tabs.Item onClick={() => handleTabClick('details')} selected={!tab || tab === 'details'}> {t('Details')} </Tabs.Item> {isAdminUser && context !== 'private' && ( - <Tabs.Item onClick={(): void => handleTabClick('requests')} selected={tab === 'requests'}> + <Tabs.Item onClick={() => handleTabClick('requests')} selected={tab === 'requests'}> {t('Requests')} </Tabs.Item> )} {isSecurityVisible && ( - <Tabs.Item onClick={(): void => handleTabClick('security')} selected={tab === 'security'}> + <Tabs.Item onClick={() => handleTabClick('security')} selected={tab === 'security'}> {t('Security')} </Tabs.Item> )} {context !== 'private' && ( - <Tabs.Item onClick={(): void => handleTabClick('releases')} selected={tab === 'releases'}> + <Tabs.Item onClick={() => handleTabClick('releases')} selected={tab === 'releases'}> {t('Releases')} </Tabs.Item> )} {Boolean(installed && settings && Object.values(settings).length) && isAdminUser && ( - <Tabs.Item onClick={(): void => handleTabClick('settings')} selected={tab === 'settings'}> + <Tabs.Item onClick={() => handleTabClick('settings')} selected={tab === 'settings'}> {t('Settings')} </Tabs.Item> )} {Boolean(installed) && isAdminUser && isAdminUser && ( - <Tabs.Item onClick={(): void => handleTabClick('logs')} selected={tab === 'logs'}> + <Tabs.Item onClick={() => handleTabClick('logs')} selected={tab === 'logs'}> {t('Logs')} </Tabs.Item> )} diff --git a/apps/meteor/client/views/marketplace/AppInstallPage.js b/apps/meteor/client/views/marketplace/AppInstallPage.js index 41c64ca0479c..860a09d6ab75 100644 --- a/apps/meteor/client/views/marketplace/AppInstallPage.js +++ b/apps/meteor/client/views/marketplace/AppInstallPage.js @@ -1,13 +1,12 @@ import { Button, ButtonGroup, Icon, Field, FieldGroup, TextInput, Throbber } from '@rocket.chat/fuselage'; import { useSetModal, - useRoute, - useQueryStringParameter, useEndpoint, useUpload, useTranslation, - useCurrentRoute, useRouteParameter, + useRouter, + useSearchParameter, } from '@rocket.chat/ui-contexts'; import React, { useCallback, useEffect, useState } from 'react'; @@ -30,20 +29,14 @@ function AppInstallPage() { const reload = useAppsReload(); - const [currentRouteName] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - - const router = useRoute(currentRouteName); - const upgradeRoute = useRoute('upgrade'); + const router = useRouter(); const context = useRouteParameter('context'); const setModal = useSetModal(); - const appId = useQueryStringParameter('id'); - const queryUrl = useQueryStringParameter('url'); + const appId = useSearchParameter('id'); + const queryUrl = useSearchParameter('url'); const [installing, setInstalling] = useState(false); @@ -87,7 +80,14 @@ function AppInstallPage() { handleAPIError(e); } - router.push({ context: 'private', page: 'info', id: appId || app.app.id }); + router.navigate({ + name: 'marketplace', + params: { + context: 'private', + page: 'info', + id: appId || app.app.id, + }, + }); reload(); @@ -177,7 +177,12 @@ function AppInstallPage() { handleClose={cancelAction} handleConfirm={() => uploadFile(appFile, manifest)} handleEnableUnlimitedApps={() => { - upgradeRoute.push({ type: 'go-fully-featured-registered' }); + router.navigate({ + name: 'upgrade', + params: { + type: 'go-fully-featured-registered', + }, + }); setModal(null); }} />, @@ -185,7 +190,13 @@ function AppInstallPage() { }; const handleCancel = () => { - router.push({ context, page: 'list' }); + router.navigate({ + name: 'marketplace', + params: { + context, + page: 'list', + }, + }); }; return ( diff --git a/apps/meteor/client/views/marketplace/AppMenu.js b/apps/meteor/client/views/marketplace/AppMenu.js index 9754f099afa8..b243dde200bd 100644 --- a/apps/meteor/client/views/marketplace/AppMenu.js +++ b/apps/meteor/client/views/marketplace/AppMenu.js @@ -3,11 +3,10 @@ import { useSetModal, useEndpoint, useTranslation, - useRoute, useRouteParameter, useToastMessageDispatch, - useCurrentRoute, usePermission, + useRouter, } from '@rocket.chat/ui-contexts'; import React, { useMemo, useCallback, useState } from 'react'; import semver from 'semver'; @@ -26,12 +25,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); const setModal = useSetModal(); - - const [currentRouteName, currentRouteParams] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - const router = useRoute(currentRouteName); + const router = useRouter(); const context = useRouteParameter('context'); const currentTab = useRouteParameter('tab'); @@ -128,7 +122,16 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { }, [app, isSubscribed, setModal, closeModal, openIncompatibleModal, buildExternalUrl, syncApp]); const handleViewLogs = useCallback(() => { - router.push({ context, page: 'info', id: app.id, version: app.version, tab: 'logs' }); + router.navigate({ + name: 'marketplace', + params: { + context, + page: 'info', + id: app.id, + version: app.version, + tab: 'logs', + }, + }); }, [app.id, app.version, context, router]); const handleDisable = useCallback(() => { @@ -163,7 +166,13 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { if (success) { dispatchToastMessage({ type: 'success', message: `${app.name} uninstalled` }); if (context === 'details' && currentTab !== 'details') { - router.replace({ ...currentRouteParams, tab: 'details' }); + router.navigate( + { + name: 'marketplace', + params: { ...router.getRouteParameters(), tab: 'details' }, + }, + { replace: true }, + ); } } } catch (error) { @@ -209,10 +218,10 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { <WarningModal close={closeModal} confirm={uninstall} text={t('Apps_Marketplace_Uninstall_App_Prompt')} confirmText={t('Yes')} />, ); }, [ + isSubscribed, appCountQuery.data, app.migrated, app.name, - isSubscribed, setModal, closeModal, t, @@ -221,7 +230,6 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { context, currentTab, router, - currentRouteParams, handleSubscription, ]); diff --git a/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx b/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx index db3291b643a4..2b8986678537 100644 --- a/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx +++ b/apps/meteor/client/views/marketplace/AppsList/AppRow.tsx @@ -2,7 +2,7 @@ import type { App } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Badge, Box, Palette } from '@rocket.chat/fuselage'; import { useBreakpoints } from '@rocket.chat/fuselage-hooks'; -import { useCurrentRoute, useRoute, useRouteParameter } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import type { KeyboardEvent, MouseEvent, ReactElement } from 'react'; import React, { memo } from 'react'; import semver from 'semver'; @@ -16,27 +16,29 @@ import BundleChips from '../BundleChips'; const AppRow = (props: App): ReactElement => { const { name, id, shortDescription, iconFileData, marketplaceVersion, iconFileContent, installed, bundledIn, version } = props; const breakpoints = useBreakpoints(); - const [currentRouteName] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - const router = useRoute(currentRouteName); + + const router = useRouter(); const context = useRouteParameter('context'); const isMobile = !breakpoints.includes('md'); - const handleNavigateToAppInfo = (): void => { - context && - router.push({ + const handleNavigateToAppInfo = () => { + if (!context) { + return; + } + router.navigate({ + name: 'marketplace', + params: { context, page: 'info', version: marketplaceVersion || version, id, tab: 'details', - }); + }, + }); }; - const handleKeyDown = (e: KeyboardEvent<HTMLOrSVGElement>): void => { + const handleKeyDown = (e: KeyboardEvent<HTMLOrSVGElement>) => { if (!['Enter', 'Space'].includes(e.nativeEvent.code)) { return; } @@ -44,7 +46,7 @@ const AppRow = (props: App): ReactElement => { handleNavigateToAppInfo(); }; - const preventClickPropagation = (e: MouseEvent<HTMLOrSVGElement>): void => { + const preventClickPropagation = (e: MouseEvent<HTMLOrSVGElement>) => { e.stopPropagation(); }; diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPage.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPage.tsx index e46c08805086..4f2f28c8bd99 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPage.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPage.tsx @@ -1,4 +1,4 @@ -import { useTranslation, useCurrentRoute, useRouteParameter } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRouteParameter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -11,15 +11,11 @@ type AppsContext = 'explore' | 'installed' | 'enterprise' | 'private'; const AppsPage = (): ReactElement => { const t = useTranslation(); - const [currentRouteName] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - const context = useRouteParameter('context'); + const context = useRouteParameter('context') as AppsContext; return ( <Page background='tint'> - <MarketplaceHeader title={t(`Apps_context_${context as AppsContext}`)} /> + <MarketplaceHeader title={t(`Apps_context_${context}`)} /> <Page.Content paddingInline='0'> <AppsPageContent /> </Page.Content> diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx index 1e2c79fe40f1..e9b91b663d8d 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx @@ -1,5 +1,5 @@ import { useDebouncedState } from '@rocket.chat/fuselage-hooks'; -import { useCurrentRoute, useRoute, useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect, useMemo, useState, useCallback } from 'react'; @@ -27,11 +27,7 @@ const AppsPageContent = (): ReactElement => { const [text, setText] = useDebouncedState('', 500); const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination(); - const [currentRouteName] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No current route name'); - } - const router = useRoute(currentRouteName); + const router = useRouter(); const context = useRouteParameter('context'); @@ -119,8 +115,14 @@ const AppsPageContent = (): ReactElement => { sortFilterStructure.items.find((item) => item.checked)?.id !== 'mru' || selectedCategories.length > 0; - const handleReturn = (): void => { - router.push({ context: 'explore', page: 'list' }); + const handleReturn = () => { + router.navigate({ + name: 'marketplace', + params: { + context: 'explore', + page: 'list', + }, + }); }; const toggleInitialSortOption = useCallback((isRequested: boolean) => { diff --git a/apps/meteor/client/views/marketplace/MarketplaceSidebar.tsx b/apps/meteor/client/views/marketplace/MarketplaceSidebar.tsx index 93c8bb66779f..df851c0c1d3d 100644 --- a/apps/meteor/client/views/marketplace/MarketplaceSidebar.tsx +++ b/apps/meteor/client/views/marketplace/MarketplaceSidebar.tsx @@ -1,4 +1,4 @@ -import { useRoutePath, useCurrentRoute, useTranslation, useLayout } from '@rocket.chat/ui-contexts'; +import { useTranslation, useLayout, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { memo } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; @@ -14,14 +14,12 @@ const MarketplaceSidebar = (): ReactElement => { const { sidebar } = useLayout(); - const currentRoute = useCurrentRoute(); - const [currentRouteName, currentRouteParams, currentQueryStringParams] = currentRoute; - const currentPath = useRoutePath(currentRouteName ?? '', currentRouteParams, currentQueryStringParams); + const currentPath = useCurrentRoutePath(); return ( <SettingsProvider privileged> <Sidebar> - <Sidebar.Header onClose={sidebar.close} title={<>{t('Marketplace')}</>} /> + <Sidebar.Header onClose={sidebar.close} title={t('Marketplace')} /> <Sidebar.Content> <SidebarItemsAssembler items={items} currentPath={currentPath} /> </Sidebar.Content> diff --git a/apps/meteor/client/views/marketplace/routes.tsx b/apps/meteor/client/views/marketplace/routes.tsx index b3c819939bcf..881f955b1018 100644 --- a/apps/meteor/client/views/marketplace/routes.tsx +++ b/apps/meteor/client/views/marketplace/routes.tsx @@ -2,6 +2,19 @@ import { lazy } from 'react'; import { createRouteGroup } from '../../lib/createRouteGroup'; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'marketplace-index': { + pattern: '/marketplace'; + pathname: '/marketplace'; + }; + 'marketplace': { + pattern: '/marketplace/:context?/:page?/:id?/:version?/:tab?'; + pathname: `/marketplace${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}`; + }; + } +} + export const registerMarketplaceRoute = createRouteGroup( 'marketplace', '/marketplace', diff --git a/apps/meteor/client/views/marketplace/sidebarItems.tsx b/apps/meteor/client/views/marketplace/sidebarItems.tsx index 1743dc993655..bafcc4e62c58 100644 --- a/apps/meteor/client/views/marketplace/sidebarItems.tsx +++ b/apps/meteor/client/views/marketplace/sidebarItems.tsx @@ -11,32 +11,32 @@ export const { subscribeToSidebarItems: subscribeToMarketplaceSidebarItems, } = createSidebarItems([ { - href: 'marketplace/explore', + href: '/marketplace/explore', icon: 'compass', i18nLabel: 'Explore', permissionGranted: (): boolean => hasAtLeastOnePermission(['access-marketplace', 'manage-apps']), }, { - href: 'marketplace/enterprise', + href: '/marketplace/enterprise', icon: 'lightning', i18nLabel: 'Enterprise', permissionGranted: (): boolean => hasAtLeastOnePermission(['access-marketplace', 'manage-apps']), }, { - href: 'marketplace/installed', + href: '/marketplace/installed', icon: 'circle-arrow-down', i18nLabel: 'Installed', permissionGranted: (): boolean => hasAtLeastOnePermission(['access-marketplace', 'manage-apps']), }, { - href: 'marketplace/requested', + href: '/marketplace/requested', icon: 'cube', i18nLabel: 'Requested', badge: () => <MarketplaceRequestBadge />, permissionGranted: (): boolean => hasPermission('manage-apps'), }, { - href: 'marketplace/private', + href: '/marketplace/private', icon: 'lock', i18nLabel: 'Private_Apps', permissionGranted: (): boolean => hasAtLeastOnePermission(['access-marketplace', 'manage-apps']), diff --git a/apps/meteor/client/views/meet/MeetPage.tsx b/apps/meteor/client/views/meet/MeetPage.tsx index c9f2faf95356..1f1f60d5295c 100644 --- a/apps/meteor/client/views/meet/MeetPage.tsx +++ b/apps/meteor/client/views/meet/MeetPage.tsx @@ -1,7 +1,6 @@ import { Button, Box, Icon, Flex } from '@rocket.chat/fuselage'; -import { useRouteParameter, useQueryStringParameter } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useSearchParameter } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import type { FC } from 'react'; import React, { useEffect, useState, useCallback } from 'react'; import { sdk } from '../../../app/utils/client/lib/SDKClient'; @@ -11,13 +10,13 @@ import PageLoading from '../root/PageLoading'; import CallPage from './CallPage'; import './styles.css'; -const MeetPage: FC = () => { +const MeetPage = () => { const [isRoomMember, setIsRoomMember] = useState(false); const [status, setStatus] = useState(null); const [visitorId, setVisitorId] = useState(null); const roomId = useRouteParameter('rid'); - const visitorToken = useQueryStringParameter('token'); - const layout = useQueryStringParameter('layout'); + const visitorToken = useSearchParameter('token'); + const layout = useSearchParameter('layout'); const [visitorName, setVisitorName] = useState(''); const [agentName, setAgentName] = useState(''); const [callStartTime, setCallStartTime] = useState(undefined); @@ -66,12 +65,15 @@ const MeetPage: FC = () => { } setupCallForAgent(); }, [setupCallForAgent, setupCallForVisitor, visitorToken]); + if (status === null) { - return <PageLoading></PageLoading>; + return <PageLoading />; } + if (!isRoomMember) { - return <NotFoundPage></NotFoundPage>; + return <NotFoundPage />; } + if (status === 'ended') { return ( <Flex.Container direction='column' justifyContent='center'> diff --git a/apps/meteor/client/views/meet/MeetRoute.tsx b/apps/meteor/client/views/meet/MeetRoute.tsx new file mode 100644 index 000000000000..bbc20356a30b --- /dev/null +++ b/apps/meteor/client/views/meet/MeetRoute.tsx @@ -0,0 +1,63 @@ +import { useEndpoint, useRouter, useSearchParameter, useToastMessageDispatch, useUserId } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { VisitorDoesNotExistError } from '../../lib/errors/VisitorDoesNotExistError'; +import PageLoading from '../root/PageLoading'; +import MeetPage from './MeetPage'; + +const MeetRoute = () => { + const router = useRouter(); + const dispatchToastMessage = useToastMessageDispatch(); + const { t } = useTranslation(); + + const uid = useUserId(); + const token = useSearchParameter('token') ?? ''; + const getVisitorByToken = useEndpoint('GET', '/v1/livechat/visitor/:token', { token }); + + const { data: hasVisitor } = useQuery( + ['meet', { token }], + async () => { + if (token) { + const result = await getVisitorByToken(); + if ('visitor' in result) { + return true; + } + + throw new VisitorDoesNotExistError(); + } + + if (!uid) { + return false; + } + + return true; + }, + { + onSuccess: (hasVisitor) => { + if (hasVisitor === false) { + router.navigate('/home'); + } + }, + onError: (error) => { + if (error instanceof VisitorDoesNotExistError) { + dispatchToastMessage({ type: 'error', message: t('core.Visitor_does_not_exist') }); + router.navigate('/home'); + return; + } + + dispatchToastMessage({ type: 'error', message: error }); + router.navigate('/home'); + }, + }, + ); + + if (!hasVisitor) { + return <PageLoading />; + } + + return <MeetPage />; +}; + +export default MeetRoute; diff --git a/apps/meteor/client/views/oauth/OAuthAuthorizationPage.tsx b/apps/meteor/client/views/oauth/OAuthAuthorizationPage.tsx index a0a4fb65d124..bf537d72b1c8 100644 --- a/apps/meteor/client/views/oauth/OAuthAuthorizationPage.tsx +++ b/apps/meteor/client/views/oauth/OAuthAuthorizationPage.tsx @@ -1,4 +1,4 @@ -import { useQueryStringParameter, useUser } from '@rocket.chat/ui-contexts'; +import { useSearchParameter, useUser } from '@rocket.chat/ui-contexts'; import RegistrationPageRouter from '@rocket.chat/web-ui-registration'; import React from 'react'; @@ -10,8 +10,8 @@ import { useOAuthAppQuery } from './hooks/useOAuthAppQuery'; const OAuthAuthorizationPage = () => { const user = useUser(); - const clientId = useQueryStringParameter('client_id'); - const redirectUri = useQueryStringParameter('redirect_uri'); + const clientId = useSearchParameter('client_id'); + const redirectUri = useSearchParameter('redirect_uri'); const oauthAppQuery = useOAuthAppQuery(clientId, { enabled: !!user, diff --git a/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx b/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx index c92bc42afe5e..cb9eb160ef63 100644 --- a/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx +++ b/apps/meteor/client/views/oauth/components/AuthorizationFormPage.tsx @@ -25,7 +25,7 @@ const AuthorizationFormPage = ({ oauthApp, redirectUri, user }: AuthorizationFor const { t } = useTranslation(); - const homeRoute = useRoute('/home'); + const homeRoute = useRoute('home'); const handleCancelButtonClick = () => { queueMicrotask(() => { diff --git a/apps/meteor/client/views/omnichannel/OmnichannelRouter.tsx b/apps/meteor/client/views/omnichannel/OmnichannelRouter.tsx index 42d61e57e8ee..eb699cea2a8b 100644 --- a/apps/meteor/client/views/omnichannel/OmnichannelRouter.tsx +++ b/apps/meteor/client/views/omnichannel/OmnichannelRouter.tsx @@ -1,4 +1,4 @@ -import { useCurrentRoute, useRoute } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { ReactNode, ReactElement } from 'react'; import React, { Suspense, useEffect } from 'react'; @@ -11,24 +11,31 @@ type OmnichannelRouterProps = { }; const OmnichannelRouter = ({ children }: OmnichannelRouterProps): ReactElement => { - const [routeName] = useCurrentRoute(); - const defaultRoute = useRoute('omnichannel-current-chats'); + const router = useRouter(); - useEffect(() => { - if (routeName === 'omnichannel-index') { - defaultRoute.push(); - } - }, [defaultRoute, routeName]); + useEffect( + () => + router.subscribeToRouteChange(() => { + if (router.getRouteName() !== 'omnichannel-index') { + return; + } - return children ? ( + router.navigate({ name: 'omnichannel-current-chats' }, { replace: true }); + }), + [router], + ); + + if (!children) { + return <PageSkeleton />; + } + + return ( <> <Suspense fallback={<PageSkeleton />}>{children}</Suspense> <SidebarPortal> <OmnichannelSidebar /> </SidebarPortal> </> - ) : ( - <PageSkeleton /> ); }; diff --git a/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx b/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx index b330fd1bbee7..3f6bb41dbf67 100644 --- a/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx +++ b/apps/meteor/client/views/omnichannel/directory/CallsContextualBarDirectory.tsx @@ -1,6 +1,6 @@ import type { IVoipRoom } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; -import { useRoute, useRouteParameter, useQueryStringParameter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRoute, useRouteParameter, useSearchParameter, useTranslation } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { useMemo } from 'react'; @@ -16,7 +16,7 @@ const CallsContextualBarDirectory: FC = () => { const bar = useRouteParameter('bar') || 'info'; const id = useRouteParameter('id'); - const token = useQueryStringParameter('token'); + const token = useSearchParameter('token'); const t = useTranslation(); diff --git a/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx b/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx index 3eb28d1ff8f5..85a50e04f5a5 100644 --- a/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx +++ b/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx @@ -1,5 +1,5 @@ import { Tabs } from '@rocket.chat/fuselage'; -import { useCurrentRoute, useRoute, useRouteParameter, usePermission, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, usePermission, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect, useCallback } from 'react'; @@ -14,22 +14,26 @@ import ContactTab from './contacts/ContactTab'; const DEFAULT_TAB = 'contacts'; const OmnichannelDirectoryPage = (): ReactElement => { - const [routeName] = useCurrentRoute(); - const tab = useRouteParameter('page'); - const directoryRoute = useRoute('omnichannel-directory'); + const router = useRouter(); + const page = useRouteParameter('page'); const canViewDirectory = usePermission('view-omnichannel-contact-center'); - useEffect(() => { - if (routeName !== 'omnichannel-directory') { - return; - } + useEffect( + () => + router.subscribeToRouteChange(() => { + if (router.getRouteName() !== 'omnichannel-directory' || !!router.getRouteParameters().page) { + return; + } - if (!tab) { - return directoryRoute.replace({ page: DEFAULT_TAB }); - } - }, [routeName, directoryRoute, tab]); + router.navigate({ + name: 'omnichannel-directory', + params: { page: DEFAULT_TAB }, + }); + }), + [router], + ); - const handleTabClick = useCallback((tab) => (): void => directoryRoute.push({ tab }), [directoryRoute]); + const handleTabClick = useCallback((tab) => () => router.navigate({ name: 'omnichannel-directory', params: { tab } }), [router]); const chatReload = () => queryClient.invalidateQueries({ queryKey: ['current-chats'] }); @@ -44,18 +48,18 @@ const OmnichannelDirectoryPage = (): ReactElement => { <Page> <Page.Header title={t('Omnichannel_Contact_Center')} /> <Tabs flexShrink={0}> - <Tabs.Item selected={tab === 'contacts'} onClick={handleTabClick('contacts')}> + <Tabs.Item selected={page === 'contacts'} onClick={handleTabClick('contacts')}> {t('Contacts')} </Tabs.Item> - <Tabs.Item selected={tab === 'chats'} onClick={handleTabClick('chats')}> + <Tabs.Item selected={page === 'chats'} onClick={handleTabClick('chats')}> {t('Chats' as 'color')} </Tabs.Item> - <Tabs.Item selected={tab === 'calls'} onClick={handleTabClick('calls')}> + <Tabs.Item selected={page === 'calls'} onClick={handleTabClick('calls')}> {t('Calls' as 'color')} </Tabs.Item> </Tabs> <Page.Content> - {(tab === 'contacts' && <ContactTab />) || (tab === 'chats' && <ChatTab />) || (tab === 'calls' && <CallTab />)} + {(page === 'contacts' && <ContactTab />) || (page === 'chats' && <ChatTab />) || (page === 'calls' && <CallTab />)} </Page.Content> </Page> <ContextualBar chatReload={chatReload} /> diff --git a/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx b/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx index 0ee7df68b6c9..8c9019f08e10 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx @@ -1,8 +1,10 @@ import { Box, Margins, ButtonGroup, Button, Icon, Divider } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useCurrentRoute, useRoute, useTranslation, useEndpoint, usePermission } from '@rocket.chat/ui-contexts'; +import type { RouteName } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useRoute, useTranslation, useEndpoint, usePermission, useRouter } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import React from 'react'; +import React, { useCallback } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; import { parseOutboundPhoneNumber } from '../../../../../../ee/client/lib/voip/parseOutboundPhoneNumber'; import ContactManagerInfo from '../../../../../../ee/client/omnichannel/ContactManagerInfo'; @@ -22,16 +24,20 @@ import { FormSkeleton } from '../../components/FormSkeleton'; type ContactInfoProps = { id: string; rid?: string; - route?: string; + route?: RouteName; }; const ContactInfo = ({ id: contactId, rid: roomId = '', route }: ContactInfoProps) => { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); - const routePath = useRoute(route || 'omnichannel-directory'); + const router = useRouter(); const liveRoute = useRoute('live'); - const [currentRouteName] = useCurrentRoute(); + + const currentRouteName = useSyncExternalStore( + router.subscribeToRouteChange, + useCallback(() => router.getRouteName(), [router]), + ); const formatDate = useFormatDate(); const isCallReady = useIsCallReady(); @@ -56,19 +62,26 @@ const ContactInfo = ({ id: contactId, rid: roomId = '', route }: ContactInfoProp return dispatchToastMessage({ type: 'error', message: t('Not_authorized') }); } - routePath.push( - route - ? { - tab: 'contact-profile', - context: 'edit', - id: roomId, - } - : { - page: 'contacts', - id: contactId, - bar: 'edit', - }, - ); + if (route) { + router.navigate({ + name: route, + params: { + tab: 'contact-profile', + context: 'edit', + id: roomId, + }, + }); + return; + } + + router.navigate({ + name: 'omnichannel-directory', + params: { + page: 'contacts', + id: contactId, + bar: 'edit', + }, + }); }); if (isInitialLoading) { diff --git a/apps/meteor/client/views/omnichannel/routes.ts b/apps/meteor/client/views/omnichannel/routes.ts index ed15c9fb6512..9d2ed96e8b1f 100644 --- a/apps/meteor/client/views/omnichannel/routes.ts +++ b/apps/meteor/client/views/omnichannel/routes.ts @@ -2,6 +2,79 @@ import { lazy } from 'react'; import { createRouteGroup } from '../../lib/createRouteGroup'; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'omnichannel-index': { + pattern: '/omnichannel'; + pathname: '/omnichannel'; + }; + 'omnichannel-installation': { + pattern: '/omnichannel/installation'; + pathname: '/omnichannel/installation'; + }; + 'omnichannel-managers': { + pattern: '/omnichannel/managers'; + pathname: '/omnichannel/managers'; + }; + 'omnichannel-agents': { + pattern: '/omnichannel/agents/:context?/:id?'; + pathname: `/omnichannel/agents${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-webhooks': { + pattern: '/omnichannel/webhooks'; + pathname: '/omnichannel/webhooks'; + }; + 'omnichannel-customfields': { + pattern: '/omnichannel/customfields/:context?/:id?'; + pathname: `/omnichannel/customfields${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-appearance': { + pattern: '/omnichannel/appearance'; + pathname: '/omnichannel/appearance'; + }; + 'omnichannel-businessHours': { + pattern: '/omnichannel/businessHours/:context?/:type?/:id?'; + pathname: `/omnichannel/businessHours${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-units': { + pattern: '/omnichannel/units/:context?/:id?'; + pathname: `/omnichannel/units${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-tags': { + pattern: '/omnichannel/tags/:context?/:id?'; + pathname: `/omnichannel/tags${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-queue': { + pattern: '/omnichannel/queue/:context?/:id?'; + pathname: `/omnichannel/queue${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-rooms': { + pattern: '/omnichannel/rooms/:context?/:id?'; + pathname: `/omnichannel/rooms${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-triggers': { + pattern: '/omnichannel/triggers/:context?/:id?'; + pathname: `/omnichannel/triggers${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-current-chats': { + pattern: '/omnichannel/current/:id?/:tab?/:context?'; + pathname: `/omnichannel/current${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-departments': { + pattern: '/omnichannel/departments/:context?/:id?/:tab?'; + pathname: `/omnichannel/departments${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-analytics': { + pattern: '/omnichannel/analytics'; + pathname: `/omnichannel/analytics`; + }; + 'omnichannel-realTime': { + pattern: '/omnichannel/realtime-monitoring'; + pathname: `/omnichannel/realtime-monitoring`; + }; + } +} + export const registerOmnichannelRoute = createRouteGroup( 'omnichannel', '/omnichannel', diff --git a/apps/meteor/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx b/apps/meteor/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx index 2501d5cba4d8..e64c8dd6df66 100644 --- a/apps/meteor/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx +++ b/apps/meteor/client/views/omnichannel/sidebar/OmnichannelSidebar.tsx @@ -1,4 +1,4 @@ -import { useRoutePath, useCurrentRoute, useTranslation, useLayout } from '@rocket.chat/ui-contexts'; +import { useTranslation, useLayout, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; import React, { memo } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; @@ -14,14 +14,12 @@ const OmnichannelSidebar: FC = () => { const { sidebar } = useLayout(); - const currentRoute = useCurrentRoute(); - const [currentRouteName, currentRouteParams, currentQueryStringParams] = currentRoute; - const currentPath = useRoutePath(currentRouteName ?? '', currentRouteParams, currentQueryStringParams); + const currentPath = useCurrentRoutePath(); return ( <SettingsProvider privileged> <Sidebar> - <Sidebar.Header onClose={sidebar.close} title={<>{t('Omnichannel')}</>} /> + <Sidebar.Header onClose={sidebar.close} title={t('Omnichannel')} /> <Sidebar.Content> <SidebarItemsAssemblerProps items={items} currentPath={currentPath} /> </Sidebar.Content> diff --git a/apps/meteor/client/views/omnichannel/sidebarItems.ts b/apps/meteor/client/views/omnichannel/sidebarItems.ts index 450680c05d16..9d3395bcbe0a 100644 --- a/apps/meteor/client/views/omnichannel/sidebarItems.ts +++ b/apps/meteor/client/views/omnichannel/sidebarItems.ts @@ -8,62 +8,62 @@ export const { subscribeToSidebarItems: subscribeToOmnichannelSidebarItems, } = createSidebarItems([ { - href: 'omnichannel/current', + href: '/omnichannel/current', i18nLabel: 'Current_Chats', permissionGranted: (): boolean => hasPermission('view-livechat-current-chats'), }, { - href: 'omnichannel-analytics', + href: '/omnichannel/analytics', i18nLabel: 'Analytics', permissionGranted: (): boolean => hasPermission('view-livechat-analytics'), }, { - href: 'omnichannel-realTime', + href: '/omnichannel/realtime-monitoring', i18nLabel: 'Real_Time_Monitoring', permissionGranted: (): boolean => hasPermission('view-livechat-real-time-monitoring'), }, { - href: 'omnichannel/managers', + href: '/omnichannel/managers', i18nLabel: 'Managers', permissionGranted: (): boolean => hasPermission('manage-livechat-managers'), }, { - href: 'omnichannel/agents', + href: '/omnichannel/agents', i18nLabel: 'Agents', permissionGranted: (): boolean => hasPermission('manage-livechat-agents'), }, { - href: 'omnichannel/departments', + href: '/omnichannel/departments', i18nLabel: 'Departments', permissionGranted: (): boolean => hasPermission('view-livechat-departments'), }, { - href: 'omnichannel-customfields', + href: '/omnichannel/customfields', i18nLabel: 'Custom_Fields', permissionGranted: (): boolean => hasPermission('view-livechat-customfields'), }, { - href: 'omnichannel-triggers', + href: '/omnichannel/triggers', i18nLabel: 'Livechat_Triggers', permissionGranted: (): boolean => hasPermission('view-livechat-triggers'), }, { - href: 'omnichannel-installation', + href: '/omnichannel/installation', i18nLabel: 'Livechat_Installation', permissionGranted: (): boolean => hasPermission('view-livechat-installation'), }, { - href: 'omnichannel-appearance', + href: '/omnichannel/appearance', i18nLabel: 'Livechat_Appearance', permissionGranted: (): boolean => hasPermission('view-livechat-appearance'), }, { - href: 'omnichannel-webhooks', + href: '/omnichannel/webhooks', i18nLabel: 'Webhooks', permissionGranted: (): boolean => hasPermission('view-livechat-webhooks'), }, { - href: 'omnichannel-businessHours', + href: '/omnichannel/businessHours', i18nLabel: 'Business_Hours', permissionGranted: (): boolean => hasPermission('view-livechat-business-hours'), }, diff --git a/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx b/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx index 854f2b05bac7..75869011b083 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx +++ b/apps/meteor/client/views/room/Header/Omnichannel/BackButton.tsx @@ -1,21 +1,27 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { HeaderToolboxAction } from '@rocket.chat/ui-client'; -import { useCurrentRoute, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; export const BackButton = ({ routeName }: { routeName?: string }): ReactElement => { + const router = useRouter(); const t = useTranslation(); - const [route = '', params] = useCurrentRoute(); - const router = useRoute(route); const back = useMutableCallback(() => { switch (routeName) { case 'omnichannel-directory': - router.replace({ ...params, bar: 'info' }); + router.navigate({ + name: 'omnichannel-directory', + params: { + ...router.getRouteParameters(), + bar: 'info', + }, + }); break; + case 'omnichannel-current-chats': - router.push(); + router.navigate({ name: 'omnichannel-current-chats' }); break; } }); diff --git a/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx b/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx index b49896d19ece..ca86752e7886 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx +++ b/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx @@ -1,7 +1,8 @@ import { HeaderToolbox } from '@rocket.chat/ui-client'; -import { useLayout, useCurrentRoute } from '@rocket.chat/ui-contexts'; +import { useLayout, useRouter } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; import BurgerMenu from '../../../../components/BurgerMenu'; import { useOmnichannelRoom } from '../../contexts/RoomContext'; @@ -27,7 +28,13 @@ type OmnichannelRoomHeaderProps = { }; const OmnichannelRoomHeader: FC<OmnichannelRoomHeaderProps> = ({ slots: parentSlot }) => { - const [name] = useCurrentRoute(); + const router = useRouter(); + + const currentRouteName = useSyncExternalStore( + router.subscribeToRouteChange, + useCallback(() => router.getRouteName(), [router]), + ); + const { isMobile } = useLayout(); const room = useOmnichannelRoom(); const toolbox = useToolboxContext(); @@ -35,16 +42,17 @@ const OmnichannelRoomHeader: FC<OmnichannelRoomHeaderProps> = ({ slots: parentSl const slots = useMemo( () => ({ ...parentSlot, - start: (!!isMobile || name === 'omnichannel-directory' || name === 'omnichannel-current-chats') && ( + start: (!!isMobile || currentRouteName === 'omnichannel-directory' || currentRouteName === 'omnichannel-current-chats') && ( <HeaderToolbox> {isMobile && <BurgerMenu />} - {<BackButton routeName={name} />} + {<BackButton routeName={currentRouteName} />} </HeaderToolbox> ), posContent: <QuickActions room={room} />, }), - [isMobile, name, parentSlot, room], + [isMobile, currentRouteName, parentSlot, room], ); + return ( <ToolboxContext.Provider value={useMemo( diff --git a/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx b/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx index 321f3522a5e5..c8b642e56d96 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx +++ b/apps/meteor/client/views/room/Header/Omnichannel/QuickActions/hooks/useQuickActions.tsx @@ -10,7 +10,7 @@ import { useEndpoint, useMethod, useTranslation, - useRoute, + useRouter, } from '@rocket.chat/ui-contexts'; import React, { useCallback, useState, useEffect } from 'react'; @@ -38,7 +38,7 @@ export const useQuickActions = ( getAction: (id: string) => void; } => { const setModal = useSetModal(); - const homeRoute = useRoute('home'); + const router = useRouter(); const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); @@ -166,14 +166,14 @@ export const useQuickActions = ( try { await forwardChat(transferData); dispatchToastMessage({ type: 'success', message: t('Transferred') }); - homeRoute.push(); + router.navigate('/home'); LegacyRoomManager.close(room.t + rid); closeModal(); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } }, - [closeModal, dispatchToastMessage, forwardChat, room.t, rid, homeRoute, t], + [closeModal, dispatchToastMessage, forwardChat, room.t, rid, router, t], ); const closeChat = useEndpoint('POST', '/v1/livechat/room.closeByUser'); @@ -213,7 +213,7 @@ export const useQuickActions = ( const returnChatToQueueMutation = useReturnChatToQueueMutation({ onSuccess: () => { LegacyRoomManager.close(room.t + rid); - homeRoute.push(); + router.navigate('/home'); }, onError: (error) => { dispatchToastMessage({ type: 'error', message: error }); diff --git a/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx b/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx index 4660cb72af2e..ec7beb37dda5 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx +++ b/apps/meteor/client/views/room/Header/Omnichannel/VoipRoomHeader.tsx @@ -1,8 +1,9 @@ import type { IVoipRoom } from '@rocket.chat/core-typings'; import { HeaderToolbox } from '@rocket.chat/ui-client'; -import { useLayout, useCurrentRoute } from '@rocket.chat/ui-contexts'; +import { useLayout, useRouter } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; import { parseOutboundPhoneNumber } from '../../../../../ee/client/lib/voip/parseOutboundPhoneNumber'; import BurgerMenu from '../../../../components/BurgerMenu'; @@ -17,21 +18,27 @@ type VoipRoomHeaderProps = { } & Omit<RoomHeaderProps, 'room'>; const VoipRoomHeader: FC<VoipRoomHeaderProps> = ({ slots: parentSlot, room }) => { - const [name] = useCurrentRoute(); + const router = useRouter(); + + const currentRouteName = useSyncExternalStore( + router.subscribeToRouteChange, + useCallback(() => router.getRouteName(), [router]), + ); + const { isMobile } = useLayout(); const toolbox = useToolboxContext(); const slots = useMemo( () => ({ ...parentSlot, - start: (!!isMobile || name === 'omnichannel-directory') && ( + start: (!!isMobile || currentRouteName === 'omnichannel-directory') && ( <HeaderToolbox> {isMobile && <BurgerMenu />} - {name === 'omnichannel-directory' && <BackButton />} + {currentRouteName === 'omnichannel-directory' && <BackButton />} </HeaderToolbox> ), }), - [isMobile, name, parentSlot], + [isMobile, currentRouteName, parentSlot], ); return ( <ToolboxContext.Provider diff --git a/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts b/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts index e8144477867c..40712c262398 100644 --- a/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts +++ b/apps/meteor/client/views/room/MessageList/hooks/useJumpToMessage.ts @@ -1,7 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import { RouterContext } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import type { RefObject } from 'react'; -import { useContext, useLayoutEffect } from 'react'; +import { useLayoutEffect } from 'react'; import { useMessageListJumpToMessageParam, useMessageListScroll } from '../../../../components/message/list/MessageListContext'; import { setHighlightMessage, clearHighlightMessage } from '../providers/messageHighlightSubscription'; @@ -12,7 +12,7 @@ const SCROLL_EXTRA_OFFSET = 60; export const useJumpToMessage = (messageId: IMessage['_id'], messageRef: RefObject<HTMLDivElement>): void => { const jumpToMessageParam = useMessageListJumpToMessageParam(); const scroll = useMessageListScroll(); - const router = useContext(RouterContext); + const router = useRouter(); useLayoutEffect(() => { if (jumpToMessageParam !== messageId || !messageRef.current || !scroll) { @@ -34,7 +34,15 @@ export const useJumpToMessage = (messageId: IMessage['_id'], messageRef: RefObje return { top: newScrollPosition, behavior: 'smooth' }; }); - router.setQueryString(({ msg: _, ...params }) => params); + const search = router.getSearchParameters(); + delete search.msg; + router.navigate( + { + pathname: router.getLocationPathname(), + search, + }, + { replace: true }, + ); setHighlightMessage(messageId); setTimeout(clearHighlightMessage, 2000); diff --git a/apps/meteor/client/views/room/MessageList/hooks/useMessages.ts b/apps/meteor/client/views/room/MessageList/hooks/useMessages.ts index a4a52fe84575..72a85b45235c 100644 --- a/apps/meteor/client/views/room/MessageList/hooks/useMessages.ts +++ b/apps/meteor/client/views/room/MessageList/hooks/useMessages.ts @@ -23,7 +23,7 @@ export const useMessages = ({ rid }: { rid: IRoom['_id'] }): IMessage[] => { const hideSysMessages = useStableArray(mergeHideSysMessages(hideSysMesSetting, hideRoomSysMes)); - const query: Mongo.Query<IMessage> = useMemo( + const query: Mongo.Selector<IMessage> = useMemo( () => ({ rid, _hidden: { $ne: true }, diff --git a/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx b/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx index e23ffeea7bf6..8bc571c3afb6 100644 --- a/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx +++ b/apps/meteor/client/views/room/MessageList/providers/MessageListProvider.tsx @@ -1,6 +1,6 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { isMessageReactionsNormalized, isThreadMainMessage } from '@rocket.chat/core-typings'; -import { useLayout, useUser, useUserPreference, useSetting, useEndpoint, useQueryStringParameter } from '@rocket.chat/ui-contexts'; +import { useLayout, useUser, useUserPreference, useSetting, useEndpoint, useSearchParameter } from '@rocket.chat/ui-contexts'; import type { VFC, ReactNode } from 'react'; import React, { useMemo, memo } from 'react'; @@ -52,7 +52,7 @@ const MessageListProvider: VFC<MessageListProviderProps> = ({ children, scrollMe const { katexEnabled, katexDollarSyntaxEnabled, katexParenthesisSyntaxEnabled } = useKatex(); const hasSubscription = Boolean(subscription); - const msgParameter = useQueryStringParameter('msg'); + const msgParameter = useSearchParameter('msg'); useLoadSurroundingMessages(msgParameter); diff --git a/apps/meteor/client/views/room/RoomRoute.tsx b/apps/meteor/client/views/room/RoomRoute.tsx new file mode 100644 index 000000000000..a0e67d8bf800 --- /dev/null +++ b/apps/meteor/client/views/room/RoomRoute.tsx @@ -0,0 +1,29 @@ +import type { RoomType } from '@rocket.chat/core-typings'; +import { useRouter } from '@rocket.chat/ui-contexts'; +import React, { useEffect, useState } from 'react'; + +import RoomOpener from './RoomOpener'; + +type RoomRouteProps = { + extractOpenRoomParams: (routeParams: Record<string, string | null | undefined>) => { + type: RoomType; + reference: string; + }; +}; + +const RoomRoute = ({ extractOpenRoomParams }: RoomRouteProps) => { + const router = useRouter(); + const [params, setParams] = useState(() => extractOpenRoomParams(router.getRouteParameters())); + + useEffect( + () => + router.subscribeToRouteChange(() => { + setParams(extractOpenRoomParams(router.getRouteParameters())); + }), + [extractOpenRoomParams, router], + ); + + return <RoomOpener {...params} />; +}; + +export default RoomRoute; diff --git a/apps/meteor/client/views/room/components/body/RoomBody.tsx b/apps/meteor/client/views/room/components/body/RoomBody.tsx index c2ea5d70d4bb..aaa0d8a6318b 100644 --- a/apps/meteor/client/views/room/components/body/RoomBody.tsx +++ b/apps/meteor/client/views/room/components/body/RoomBody.tsx @@ -1,10 +1,10 @@ import type { IMessage, IUser } from '@rocket.chat/core-typings'; import { isEditedMessage } from '@rocket.chat/core-typings'; import { - useCurrentRoute, usePermission, - useQueryStringParameter, useRole, + useRouter, + useSearchParameter, useSetting, useTranslation, useUser, @@ -258,7 +258,7 @@ const RoomBody = (): ReactElement => { }; }, [sendToBottomIfNecessary]); - const [routeName] = useCurrentRoute(); + const router = useRouter(); const roomRef = useRef(room); roomRef.current = room; @@ -273,16 +273,21 @@ const RoomBody = (): ReactElement => { [room._id], ); - useEffect(() => { - if (!routeName || !roomCoordinator.isRouteNameKnown(routeName)) { - return; - } + useEffect( + () => + router.subscribeToRouteChange(() => { + const routeName = router.getRouteName(); + if (!routeName || !roomCoordinator.isRouteNameKnown(routeName)) { + return; + } - debouncedReadMessageRead(); - if (subscribed && (subscription?.alert || subscription?.unread)) { - readMessage.refreshUnreadMark(room._id); - } - }, [debouncedReadMessageRead, room._id, routeName, subscribed, subscription?.alert, subscription?.unread]); + debouncedReadMessageRead(); + if (subscribed && (subscription?.alert || subscription?.unread)) { + readMessage.refreshUnreadMark(room._id); + } + }), + [debouncedReadMessageRead, room._id, router, subscribed, subscription?.alert, subscription?.unread], + ); useEffect(() => { if (!subscribed) { @@ -481,7 +486,7 @@ const RoomBody = (): ReactElement => { [chat], ); - const replyMID = useQueryStringParameter('reply'); + const replyMID = useSearchParameter('reply'); useEffect(() => { if (!replyMID) { diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js index eb344108278b..90234643f353 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js @@ -24,7 +24,7 @@ import { useRole, useMethod, useTranslation, - useRoute, + useRouter, } from '@rocket.chat/ui-contexts'; import React, { useCallback, useMemo, useRef } from 'react'; @@ -134,7 +134,7 @@ function EditChannel({ room, onClickClose, onClickBack }) { const maxAgeDefault = useSetting(`RetentionPolicy_MaxAge_${typeMap[room.t]}`) || 30; const saveData = useRef({}); - const router = useRoute('home'); + const router = useRouter(); const onChange = useCallback(({ initialValue, value, key }) => { const { current } = saveData; @@ -277,7 +277,7 @@ function EditChannel({ room, onClickClose, onClickBack }) { const onConfirm = async () => { await deleteRoom(room._id); onCancel(); - router.push({}); + router.navigate('/home'); }; setModal( diff --git a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomDelete.tsx b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomDelete.tsx index 2d8b135b7d34..b812e896bab9 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomDelete.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomDelete.tsx @@ -1,7 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useToastMessageDispatch, useRoute, useTranslation, useEndpoint, usePermission } from '@rocket.chat/ui-contexts'; +import { useSetModal, useToastMessageDispatch, useTranslation, useEndpoint, usePermission, useRouter } from '@rocket.chat/ui-contexts'; import React from 'react'; import GenericModal from '../../../../../../components/GenericModal'; @@ -11,7 +11,7 @@ export const useRoomDelete = (room: IRoom, resetState?: () => void) => { const t = useTranslation(); const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); - const router = useRoute('home'); + const router = useRouter(); const hasPermissionToDelete = usePermission(room.t === 'c' ? 'delete-c' : 'delete-p', room._id); const canDelete = isRoomFederated(room) ? false : hasPermissionToDelete; @@ -27,7 +27,7 @@ export const useRoomDelete = (room: IRoom, resetState?: () => void) => { return resetState(); } - router.push({}); + router.navigate('/home'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } diff --git a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomHide.tsx b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomHide.tsx index 5047e785c8e3..fde85f426396 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomHide.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomHide.tsx @@ -1,7 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetModal, useToastMessageDispatch, useRoute, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; +import { useSetModal, useToastMessageDispatch, useMethod, useTranslation, useRouter } from '@rocket.chat/ui-contexts'; import React from 'react'; import { UiTextContext } from '../../../../../../../definition/IRoomTypeConfig'; @@ -13,13 +13,13 @@ export const useRoomHide = (room: IRoom) => { const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); const hideRoom = useMethod('hideRoom'); - const router = useRoute('home'); + const router = useRouter(); const handleHide = useMutableCallback(async () => { const hide = async () => { try { await hideRoom(room._id); - router.push({}); + router.navigate('/home'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } diff --git a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomLeave.tsx b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomLeave.tsx index 03a652cd58ee..6b842c1fc173 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomLeave.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/hooks/actions/useRoomLeave.tsx @@ -1,7 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetModal, useToastMessageDispatch, useRoute, useMethod, useTranslation, usePermission } from '@rocket.chat/ui-contexts'; +import { useRouter, useSetModal, useToastMessageDispatch, useMethod, useTranslation, usePermission } from '@rocket.chat/ui-contexts'; import React from 'react'; import { LegacyRoomManager } from '../../../../../../../app/ui-utils/client'; @@ -15,7 +15,7 @@ export const useRoomLeave = (room: IRoom, joined = true) => { const setModal = useSetModal(); const dispatchToastMessage = useToastMessageDispatch(); const leaveRoom = useMethod('leaveRoom'); - const router = useRoute('home'); + const router = useRouter(); const canLeave = usePermission(room.t === 'c' ? 'leave-c' : 'leave-p') && room.cl !== false && joined; @@ -23,7 +23,7 @@ export const useRoomLeave = (room: IRoom, joined = true) => { const leaveAction = async () => { try { await leaveRoom(room._id); - router.push({}); + router.navigate('/home'); LegacyRoomManager.close(room._id); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); diff --git a/apps/meteor/client/views/room/contextualBar/Threads/hooks/useGetMessageByID.ts b/apps/meteor/client/views/room/contextualBar/Threads/hooks/useGetMessageByID.ts index 64a6fcb50bc5..c9898a172495 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/hooks/useGetMessageByID.ts +++ b/apps/meteor/client/views/room/contextualBar/Threads/hooks/useGetMessageByID.ts @@ -15,7 +15,7 @@ export const useGetMessageByID = () => { const { message: rawMessage } = await getMessage({ msgId: mid }); const mappedMessage = mapMessageFromApi(rawMessage); const message = (await onClientMessageReceived(mappedMessage)) || mappedMessage; - Messages.upsert({ _id: message._id }, { $set: message }); + Messages.upsert({ _id: message._id }, { $set: message as any }); return message; } catch (error) { if (typeof error === 'object' && error !== null && 'success' in error) { diff --git a/apps/meteor/client/views/room/contextualBar/Threads/hooks/useLegacyThreadMessageJump.ts b/apps/meteor/client/views/room/contextualBar/Threads/hooks/useLegacyThreadMessageJump.ts index 254afbf67691..551d08d5f142 100644 --- a/apps/meteor/client/views/room/contextualBar/Threads/hooks/useLegacyThreadMessageJump.ts +++ b/apps/meteor/client/views/room/contextualBar/Threads/hooks/useLegacyThreadMessageJump.ts @@ -1,22 +1,30 @@ -import { useCurrentRoute, useQueryStringParameter, useRoute } from '@rocket.chat/ui-contexts'; +import { useRouter, useSearchParameter } from '@rocket.chat/ui-contexts'; import { useRef, useEffect } from 'react'; import { waitForElement } from '../../../../../lib/utils/waitForElement'; import { clearHighlightMessage, setHighlightMessage } from '../../../MessageList/providers/messageHighlightSubscription'; export const useLegacyThreadMessageJump = ({ enabled = true }: { enabled?: boolean }) => { - const mid = useQueryStringParameter('msg'); - - const [currentRouteName, currentRouteParams, currentRouteQueryStringParams] = useCurrentRoute(); - if (!currentRouteName) { - throw new Error('No route name'); - } - const currentRoute = useRoute(currentRouteName); + const router = useRouter(); + const mid = useSearchParameter('msg'); const clearQueryStringParameter = () => { - const newQueryStringParams = { ...currentRouteQueryStringParams }; - delete newQueryStringParams.msg; - currentRoute.replace(currentRouteParams, newQueryStringParams); + const name = router.getRouteName(); + + if (!name) { + return; + } + + const { msg: _, ...search } = router.getSearchParameters(); + + router.navigate( + { + name, + params: router.getRouteParameters(), + search, + }, + { replace: true }, + ); }; const parentRef = useRef<HTMLElement>(null); diff --git a/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts b/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts index b1c844722909..238ee807744f 100644 --- a/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts +++ b/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts @@ -1,5 +1,5 @@ import type { IUIKitContextualBarInteraction } from '@rocket.chat/apps-engine/definition/uikit'; -import { useCurrentRoute } from '@rocket.chat/ui-contexts'; +import { useRouteParameter } from '@rocket.chat/ui-contexts'; import { useEffect, useState } from 'react'; import { getUserInteractionPayloadByViewId } from '../../../../app/ui-message/client/ActionManager'; @@ -13,13 +13,12 @@ type AppsContextualBarData = { }; export const useAppsContextualBar = (): AppsContextualBarData | undefined => { - const [, params] = useCurrentRoute(); const [payload, setPayload] = useState<IUIKitContextualBarInteraction>(); const [appId, setAppId] = useState<string>(); const { _id: roomId } = useRoom(); - const viewId = params?.context; + const viewId = useRouteParameter('context'); useEffect(() => { if (viewId) { diff --git a/apps/meteor/client/views/room/hooks/useGoToRoom.ts b/apps/meteor/client/views/room/hooks/useGoToRoom.ts index 1b4d8650a232..330dc338fde4 100644 --- a/apps/meteor/client/views/room/hooks/useGoToRoom.ts +++ b/apps/meteor/client/views/room/hooks/useGoToRoom.ts @@ -1,13 +1,13 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useMethod } from '@rocket.chat/ui-contexts'; -import { FlowRouter } from 'meteor/kadira:flow-router'; +import { useMethod, useRouter } from '@rocket.chat/ui-contexts'; import { ChatSubscription } from '../../../../app/models/client'; import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; export const useGoToRoom = ({ replace = false }: { replace?: boolean } = {}): ((rid: IRoom['_id']) => void) => { const getRoomById = useMethod('getRoomById'); + const router = useRouter(); // TODO: remove params recycling return useMutableCallback(async (rid) => { @@ -15,16 +15,14 @@ export const useGoToRoom = ({ replace = false }: { replace?: boolean } = {}): (( return; } - const go = (fn: () => void) => (replace ? FlowRouter.withReplaceState(fn) : fn()); - const subscription: ISubscription | undefined = ChatSubscription.findOne({ rid }); if (subscription) { - go(() => roomCoordinator.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams)); + roomCoordinator.openRouteLink(subscription.t, subscription, router.getSearchParameters(), { replace }); return; } const room = await getRoomById(rid); - go(() => roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }, FlowRouter.current().queryParams)); + roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }, router.getSearchParameters(), { replace }); }); }; diff --git a/apps/meteor/client/views/room/hooks/useGoToThread.ts b/apps/meteor/client/views/room/hooks/useGoToThread.ts index c37f7d6445c5..9a14ce2e28c2 100644 --- a/apps/meteor/client/views/room/hooks/useGoToThread.ts +++ b/apps/meteor/client/views/room/hooks/useGoToThread.ts @@ -1,23 +1,29 @@ import type { IMessage, IRoom } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useCurrentRoute, useRoute } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; export const useGoToThread = ({ replace = false }: { replace?: boolean } = {}): ((params: { rid: IRoom['_id']; tmid: IMessage['_id']; msg?: IMessage['_id']; }) => void) => { - const [routeName, params, queryParams] = useCurrentRoute(); - - if (!routeName) { - throw new Error('Route name is not defined'); - } - - const roomRoute = useRoute(routeName); - const go = replace ? roomRoute.replace : roomRoute.push; + const router = useRouter(); // TODO: remove params recycling return useMutableCallback(({ rid, tmid, msg }) => { - go({ rid, ...params, tab: 'thread', context: tmid }, { ...queryParams, ...(msg && { msg }) }); + const routeName = router.getRouteName(); + + if (!routeName) { + throw new Error('Route name is not defined'); + } + + router.navigate( + { + name: routeName, + params: { rid, ...router.getRouteParameters(), tab: 'thread', context: tmid }, + search: { ...router.getSearchParameters(), ...(msg && { msg }) }, + }, + { replace }, + ); }); }; diff --git a/apps/meteor/client/views/room/hooks/useGoToThreadList.ts b/apps/meteor/client/views/room/hooks/useGoToThreadList.ts index b0677118d035..c44df790f748 100644 --- a/apps/meteor/client/views/room/hooks/useGoToThreadList.ts +++ b/apps/meteor/client/views/room/hooks/useGoToThreadList.ts @@ -1,19 +1,28 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useCurrentRoute, useRoute } from '@rocket.chat/ui-contexts'; +import { useRouter } from '@rocket.chat/ui-contexts'; import { useRoom } from '../contexts/RoomContext'; export const useGoToThreadList = ({ replace = false }: { replace?: boolean } = {}): (() => void) => { + const router = useRouter(); const room = useRoom(); - const [routeName, { context, ...params } = { context: '' }, queryParams] = useCurrentRoute(); - if (!routeName) { - throw new Error('Route name is not defined'); - } - - const roomRoute = useRoute(routeName); - const go = replace ? roomRoute.replace : roomRoute.push; return useMutableCallback(() => { - go({ rid: room._id, ...params, tab: 'thread' }, queryParams); + const routeName = router.getRouteName(); + + if (!routeName) { + throw new Error('Route name is not defined'); + } + + const { context, ...params } = router.getRouteParameters(); + + router.navigate( + { + name: routeName, + params: { rid: room._id, ...params, tab: 'thread' }, + search: router.getSearchParameters(), + }, + { replace }, + ); }); }; diff --git a/apps/meteor/client/views/room/hooks/useOpenRoom.ts b/apps/meteor/client/views/room/hooks/useOpenRoom.ts index 288767ccde53..4e109759593a 100644 --- a/apps/meteor/client/views/room/hooks/useOpenRoom.ts +++ b/apps/meteor/client/views/room/hooks/useOpenRoom.ts @@ -42,8 +42,8 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st throw new RoomNotFoundError(undefined, { type, reference }); } - const $set: Record<string, unknown> = {}; - const $unset: Record<string, unknown> = {}; + const $set: any = {}; + const $unset: any = {}; for (const key of Object.keys(roomFields)) { if (key in roomData) { diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts index 03db347ad9a0..00df2f1225ac 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useRedirectModerationConsole.ts @@ -6,7 +6,7 @@ import type { Action } from '../../../../hooks/useActionSpread'; export const useRedirectModerationConsole = (uid: IUser['_id']): Action | undefined => { const t = useTranslation(); const hasPermissionToView = usePermission('view-moderation-console'); - const router = useRoute('/admin/moderation-console/info/:uid'); + const router = useRoute('moderation-console'); // only rediret if user has permission else return undefined if (!hasPermissionToView) { diff --git a/apps/meteor/client/views/room/providers/RoomProvider.tsx b/apps/meteor/client/views/room/providers/RoomProvider.tsx index 3fba38e727be..b2935f4702ea 100644 --- a/apps/meteor/client/views/room/providers/RoomProvider.tsx +++ b/apps/meteor/client/views/room/providers/RoomProvider.tsx @@ -1,6 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; -import { usePermission, useRoute, useStream, useUserId } from '@rocket.chat/ui-contexts'; +import { usePermission, useStream, useUserId, useRouter } from '@rocket.chat/ui-contexts'; import { useQueryClient } from '@tanstack/react-query'; import type { ReactNode, ContextType, ReactElement } from 'react'; import React, { useMemo, memo, useEffect, useCallback } from 'react'; @@ -49,12 +49,12 @@ const RoomProvider = ({ rid, children }: RoomProviderProps): ReactElement => { }, [subscribeToRoom, rid, queryClient, room]); // TODO: the following effect is a workaround while we don't have a general and definitive solution for it - const homeRoute = useRoute('home'); + const router = useRouter(); useEffect(() => { if (isSuccess && !room) { - homeRoute.push(); + router.navigate('/home'); } - }, [isSuccess, room, homeRoute]); + }, [isSuccess, room, router]); // TODO: Review the necessity of this effect when we move away from cached collections useEffect(() => { @@ -68,7 +68,7 @@ const RoomProvider = ({ rid, children }: RoomProviderProps): ReactElement => { queryClient.removeQueries(['rooms', { reference: room._id, type: 'l' }]); queryClient.removeQueries(['/v1/rooms.info', room._id]); } - }, [homeRoute, isLivechatAdmin, queryClient, userId, room]); + }, [isLivechatAdmin, queryClient, userId, room]); const subscriptionQuery = useReactiveQuery(['subscriptions', { rid }], () => ChatSubscription.findOne({ rid }) ?? null); diff --git a/apps/meteor/client/views/room/providers/ToolboxProvider.tsx b/apps/meteor/client/views/room/providers/ToolboxProvider.tsx index 810509f13f69..27fe6abbb176 100644 --- a/apps/meteor/client/views/room/providers/ToolboxProvider.tsx +++ b/apps/meteor/client/views/room/providers/ToolboxProvider.tsx @@ -1,6 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { useDebouncedState, useMutableCallback, useSafely } from '@rocket.chat/fuselage-hooks'; -import { useCurrentRoute, useRoute, useUserId, useSetting } from '@rocket.chat/ui-contexts'; +import { useUserId, useSetting, useRouter, useRouteParameter } from '@rocket.chat/ui-contexts'; import type { ReactNode } from 'react'; import React, { useMemo } from 'react'; @@ -21,11 +21,10 @@ const ToolboxProvider = ({ children, room }: { children: ReactNode; room: IRoom }); const { listen, actions } = useToolboxActions(room); - const [routeName, params, queryStringParams] = useCurrentRoute(); - const router = useRoute(routeName || ''); + const router = useRouter(); - const tab = params?.tab; - const context = params?.context; + const tab = useRouteParameter('tab'); + const context = useRouteParameter('context'); const activeTabBar = useMemo( (): [ToolboxActionConfig | undefined, string?] => [tab ? (list.get(tab) as ToolboxActionConfig) : undefined, context], @@ -33,14 +32,21 @@ const ToolboxProvider = ({ children, room }: { children: ReactNode; room: IRoom ); const close = useMutableCallback(() => { - router.push( - { - ...params, + const routeName = router.getRouteName(); + + if (!routeName) { + throw new Error('Route name is not defined'); + } + + router.navigate({ + name: routeName, + params: { + ...router.getRouteParameters(), tab: '', context: '', }, - queryStringParams, - ); + search: router.getSearchParameters(), + }); }); const open = useMutableCallback((actionId: string, context?: string) => { @@ -48,16 +54,23 @@ const ToolboxProvider = ({ children, room }: { children: ReactNode; room: IRoom return close(); } - const { layout } = queryStringParams || {}; - const queryString = layout ? { layout } : undefined; - router.push( - { - ...params, + const routeName = router.getRouteName(); + + if (!routeName) { + throw new Error('Route name is not defined'); + } + + const { layout } = router.getSearchParameters(); + + router.navigate({ + name: routeName, + params: { + ...router.getRouteParameters(), tab: actionId, context: context ?? '', }, - queryString, - ); + search: layout ? { layout } : undefined, + }); }); const openRoomInfo = useMutableCallback((username?: string) => { diff --git a/apps/meteor/client/views/root/AppLayout.tsx b/apps/meteor/client/views/root/AppLayout.tsx index 4cfb9e7efbc5..5bdcd9d6a5b5 100644 --- a/apps/meteor/client/views/root/AppLayout.tsx +++ b/apps/meteor/client/views/root/AppLayout.tsx @@ -1,20 +1,20 @@ -import type { ReactElement } from 'react'; import React, { useEffect, Suspense } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; import { useAnalytics } from '../../../app/analytics/client/loadScript'; +import { useAnalyticsEventTracking } from '../../hooks/useAnalyticsEventTracking'; import { appLayout } from '../../lib/appLayout'; import PageLoading from './PageLoading'; import { useEscapeKeyStroke } from './hooks/useEscapeKeyStroke'; import { useGoogleTagManager } from './hooks/useGoogleTagManager'; import { useMessageLinkClicks } from './hooks/useMessageLinkClicks'; -const AppLayout = (): ReactElement => { +const AppLayout = () => { useEffect(() => { document.body.classList.add('color-primary-font-color', 'rcx-content--main'); return () => { - document.body.classList.add('color-primary-font-color', 'rcx-content--main'); + document.body.classList.remove('color-primary-font-color', 'rcx-content--main'); }; }, []); @@ -22,14 +22,11 @@ const AppLayout = (): ReactElement => { useGoogleTagManager(); useAnalytics(); useEscapeKeyStroke(); + useAnalyticsEventTracking(); const layout = useSyncExternalStore(appLayout.subscribe, appLayout.getSnapshot); - return ( - <> - <Suspense fallback={<PageLoading />}>{layout}</Suspense> - </> - ); + return <Suspense fallback={<PageLoading />}>{layout}</Suspense>; }; export default AppLayout; diff --git a/apps/meteor/client/views/root/IndexRoute.tsx b/apps/meteor/client/views/root/IndexRoute.tsx new file mode 100644 index 000000000000..90134e75881c --- /dev/null +++ b/apps/meteor/client/views/root/IndexRoute.tsx @@ -0,0 +1,42 @@ +import type { RouteName } from '@rocket.chat/ui-contexts'; +import { useRouter, useUser, useUserId } from '@rocket.chat/ui-contexts'; +import React, { useEffect } from 'react'; + +import PageLoading from './PageLoading'; + +const IndexRoute = () => { + const router = useRouter(); + const uid = useUserId(); + const user = useUser(); + + useEffect(() => { + if (!uid) { + router.navigate('/home'); + return; + } + + const computation = Tracker.autorun((c) => { + setTimeout(async () => { + if (user?.defaultRoom) { + const room = user.defaultRoom.split('/') as [routeName: RouteName, routeParam: string]; + router.navigate({ + name: room[0], + params: { name: room[1] }, + search: router.getSearchParameters(), + }); + } else { + router.navigate('/home'); + } + }, 0); + c.stop(); + }); + + return () => { + computation.stop(); + }; + }, [router, uid, user?.defaultRoom]); + + return <PageLoading />; +}; + +export default IndexRoute; diff --git a/apps/meteor/client/views/root/LoginRoute.tsx b/apps/meteor/client/views/root/LoginRoute.tsx new file mode 100644 index 000000000000..443e51d5cff0 --- /dev/null +++ b/apps/meteor/client/views/root/LoginRoute.tsx @@ -0,0 +1,14 @@ +import { useRouter } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +const LoginRoute = () => { + const router = useRouter(); + + useEffect(() => { + router.navigate('/home'); + }, [router]); + + return null; +}; + +export default LoginRoute; diff --git a/apps/meteor/client/views/root/LoginTokenRoute.tsx b/apps/meteor/client/views/root/LoginTokenRoute.tsx new file mode 100644 index 000000000000..3bce5901da64 --- /dev/null +++ b/apps/meteor/client/views/root/LoginTokenRoute.tsx @@ -0,0 +1,25 @@ +import { useRouter } from '@rocket.chat/ui-contexts'; +import { Accounts } from 'meteor/accounts-base'; +import { useEffect } from 'react'; + +const LoginTokenRoute = () => { + const router = useRouter(); + + useEffect(() => { + Accounts.callLoginMethod({ + methodArguments: [ + { + loginToken: router.getRouteParameters().token, + }, + ], + userCallback(error) { + console.error(error); + router.navigate('/'); + }, + }); + }, [router]); + + return null; +}; + +export default LoginTokenRoute; diff --git a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx index c4d73dc1e4e1..ee50ad826785 100644 --- a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx +++ b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx @@ -1,5 +1,5 @@ import { Box } from '@rocket.chat/fuselage'; -import { useLayout, useCurrentRoute, useRoutePath, useSetting, useCurrentModal, useRoute } from '@rocket.chat/ui-contexts'; +import { useLayout, useSetting, useCurrentModal, useRoute, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import { PaletteStyleTag } from '@rocket.chat/ui-theming/src/PaletteStyleTag'; import { SidebarPaletteStyleTag } from '@rocket.chat/ui-theming/src/SidebarPaletteStyleTag'; import type { ReactElement, ReactNode } from 'react'; @@ -9,10 +9,9 @@ import Sidebar from '../../../sidebar'; const LayoutWithSidebar = ({ children }: { children: ReactNode }): ReactElement => { const { isEmbedded: embeddedLayout } = useLayout(); - const [currentRouteName = '', currentParameters = {}] = useCurrentRoute(); const modal = useCurrentModal(); - const currentRoutePath = useRoutePath(currentRouteName, currentParameters); + const currentRoutePath = useCurrentRoutePath(); const channelRoute = useRoute('channel'); const removeSidenav = embeddedLayout && !currentRoutePath?.startsWith('/admin'); const readReceiptsEnabled = useSetting('Message_Read_Receipt_Store_Users'); diff --git a/apps/meteor/client/views/setupWizard/hooks/useRouteLock.ts b/apps/meteor/client/views/setupWizard/hooks/useRouteLock.ts index aa02b7231892..e771dea6eeb5 100644 --- a/apps/meteor/client/views/setupWizard/hooks/useRouteLock.ts +++ b/apps/meteor/client/views/setupWizard/hooks/useRouteLock.ts @@ -1,5 +1,5 @@ import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; -import { useRoute, useUserId, useUser, useSetting, useRole } from '@rocket.chat/ui-contexts'; +import { useUserId, useUser, useSetting, useRole, useRouter } from '@rocket.chat/ui-contexts'; import { useEffect, useState } from 'react'; export const useRouteLock = (): boolean => { @@ -8,7 +8,7 @@ export const useRouteLock = (): boolean => { const userId = useUserId(); const user = useDebouncedValue(useUser(), 100); const hasAdminRole = useRole('admin'); - const homeRoute = useRoute('home'); + const router = useRouter(); useEffect(() => { if (!setupWizardState) { @@ -26,12 +26,12 @@ export const useRouteLock = (): boolean => { const mustRedirect = isComplete || noUserLoggedInAndIsNotPending || userIsLoggedInButIsNotAdmin; if (mustRedirect) { - homeRoute.replace(); + router.navigate('/home'); return; } setLocked(false); - }, [homeRoute, setupWizardState, userId, user, hasAdminRole, locked]); + }, [router, setupWizardState, userId, user, hasAdminRole, locked]); return locked; }; diff --git a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js index 7c49b7dd3f34..8044d15d9380 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js +++ b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfoWithData.js @@ -2,12 +2,12 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, - useRoute, useUserId, useSetting, usePermission, useMethod, useTranslation, + useRouter, } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; @@ -61,7 +61,7 @@ const TeamsInfoWithLogic = ({ room, openEditing }) => { const hideTeam = useMethod('hideRoom'); - const router = useRoute('home'); + const router = useRouter(); const canDelete = usePermission('delete-team', room._id); const canEdit = usePermission('edit-team-channel', room._id); @@ -75,7 +75,7 @@ const TeamsInfoWithLogic = ({ room, openEditing }) => { try { await deleteTeam({ teamId: room.teamId, ...(roomsToRemove.length && { roomsToRemove }) }); dispatchToastMessage({ type: 'success', message: t('Team_has_been_deleted') }); - router.push({}); + router.navigate('/home'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } finally { @@ -97,7 +97,7 @@ const TeamsInfoWithLogic = ({ room, openEditing }) => { ...(roomsToLeave.length && { rooms: roomsToLeave }), }); dispatchToastMessage({ type: 'success', message: t('Teams_left_team_successfully') }); - router.push({}); + router.navigate('/home'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } finally { @@ -112,7 +112,7 @@ const TeamsInfoWithLogic = ({ room, openEditing }) => { const hide = async () => { try { await hideTeam(room._id); - router.push({}); + router.navigate('/home'); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); } finally { diff --git a/apps/meteor/definition/IRoomTypeConfig.ts b/apps/meteor/definition/IRoomTypeConfig.ts index 5ff2986ec397..1cf746ad1fe1 100644 --- a/apps/meteor/definition/IRoomTypeConfig.ts +++ b/apps/meteor/definition/IRoomTypeConfig.ts @@ -11,12 +11,13 @@ import type { } from '@rocket.chat/core-typings'; import type { ComponentProps } from 'react'; import type { Icon } from '@rocket.chat/fuselage'; +import type { IRouterPaths, RouteName } from '@rocket.chat/ui-contexts'; export type RoomIdentification = { rid?: IRoom['_id']; name?: string; tab?: string }; -export interface IRoomTypeRouteConfig { - name: string; - path?: string; +export interface IRoomTypeRouteConfig<TRouteName extends RouteName> { + name: TRouteName; + path?: IRouterPaths[TRouteName]['pattern']; link?: (data: RoomIdentification) => Record<string, string>; } @@ -56,7 +57,7 @@ export const UiTextContext = { export interface IRoomTypeConfig { identifier: string; - route?: IRoomTypeRouteConfig; + route?: IRoomTypeRouteConfig<RouteName>; } export interface IRoomTypeClientConfig extends IRoomTypeConfig { @@ -81,7 +82,7 @@ export interface IRoomTypeClientDirectives { room: AtLeast<IRoom, '_id' | 'name' | 'fname' | 'prid' | 'avatarETag' | 'uids' | 'usernames'> & { username?: IRoom['_id'] }, ) => string; getIcon?: (room: Partial<IRoom>) => ComponentProps<typeof Icon>['name']; - extractOpenRoomParams?: (routeParams: Record<string, string | null | undefined>) => { type: RoomType; ref: string }; + extractOpenRoomParams?: (routeParams: Record<string, string | null | undefined>) => { type: RoomType; reference: string }; findRoom: (identifier: string) => IRoom | undefined; showJoinLink: (roomId: string) => boolean; isLivechatRoom: () => boolean; diff --git a/apps/meteor/definition/externals/meteor/http.d.ts b/apps/meteor/definition/externals/meteor/http.d.ts new file mode 100644 index 000000000000..5d89f1954837 --- /dev/null +++ b/apps/meteor/definition/externals/meteor/http.d.ts @@ -0,0 +1,53 @@ +import 'meteor/http'; + +declare module 'meteor/http' { + namespace HTTP { + interface HTTPRequest { + content?: string | undefined; + data?: any; + query?: string | undefined; + params?: { [id: string]: string } | undefined; + auth?: string | undefined; + headers?: { [id: string]: string } | undefined; + timeout?: number | undefined; + followRedirects?: boolean | undefined; + } + + interface HTTPResponse { + statusCode?: number | undefined; + headers?: { [id: string]: string } | undefined; + content?: string | undefined; + data?: any; + } + + type AsyncCallback = (error: Meteor.Error | null, result?: HTTPResponse) => void; + + function del(url: string, callOptions?: HTTP.HTTPRequest, asyncCallback?: AsyncCallback): HTTP.HTTPResponse; + + function get(url: string, callOptions?: HTTP.HTTPRequest, asyncCallback?: AsyncCallback): HTTP.HTTPResponse; + + function post(url: string, callOptions?: HTTP.HTTPRequest, asyncCallback?: AsyncCallback): HTTP.HTTPResponse; + + function put(url: string, callOptions?: HTTP.HTTPRequest, asyncCallback?: AsyncCallback): HTTP.HTTPResponse; + + function call(method: string, url: string, options?: HTTP.HTTPRequest, asyncCallback?: AsyncCallback): HTTP.HTTPResponse; + + function call( + method: string, + url: string, + options?: { + content?: string | undefined; + data?: Object | undefined; + query?: string | undefined; + params?: Object | undefined; + auth?: string | undefined; + headers?: Object | undefined; + timeout?: number | undefined; + followRedirects?: boolean | undefined; + npmRequestOptions?: Object | undefined; + beforeSend?: Function | undefined; + }, + asyncCallback?: AsyncCallback, + ): HTTP.HTTPResponse; + } +} diff --git a/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts b/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts index 4c72b82d202f..8dfc0de5a01b 100644 --- a/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts +++ b/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts @@ -5,30 +5,30 @@ declare module 'meteor/kadira:flow-router' { params: Record<string, string>; queryParams: Record<string, string>; pathname: string; - oldRoute?: Route; - route: Route; + oldRoute?: Route<string, any>; + route: Route<string, any>; }; - type RouteOptions = { - name?: string; - action?: (this: Route, params?: Record<string, string>, queryParams?: Record<string, string>) => void; - subscriptions?: (this: Route, params?: Record<string, string>, queryParams?: Record<string, string>) => void; - triggersEnter?: ((context: Context, redirect: (pathDef: string) => void, stop: () => void) => void)[]; + type RouteOptions<TRouteName extends string> = { + name?: TRouteName; + action?: (this: Route, params?: Record<string, string>, queryParams?: Record<string, string | string[]>) => void; + subscriptions?: (this: Route, params?: Record<string, string>, queryParams?: Record<string, string | string[]>) => void; + triggersEnter?: ((context: Context, redirect: (path: string) => void, stop: () => void) => void)[]; triggersExit?: ((context: Context) => void)[]; }; - class Route { - constructor(router: Router, pathDef: string, options?: RouteOptions, group?: Group); + class Route<TRouteName extends string, TGroup extends Group<string> | undefined = any> { + constructor(router: Router, pathDef: string, options?: RouteOptions<TRouteName>, group?: TGroup); - options: RouteOptions; + options: RouteOptions<TRouteName>; pathDef: string; path: string; - name?: string; + name?: TRouteName; - group?: Group; + group?: TGroup; clearSubscriptions(): void; @@ -55,29 +55,38 @@ declare module 'meteor/kadira:flow-router' { registerRouteChange(currentContext: Context, routeChanging?: boolean): void; } - type GroupOptions = { - name: string; + type GroupOptions<TGroupName extends string> = { + name: TGroupName; prefix?: string; triggersEnter?: unknown[]; triggersExit?: unknown[]; - subscriptions?: (this: Route, params?: Record<string, string>, queryParams?: Record<string, string>) => void; + subscriptions?: (this: Route, params?: Record<string, string>, queryParams?: Record<string, string | string[]>) => void; }; - class Group { - constructor(router: Router, options?: GroupOptions, parent?: Group); + /** @deprecated */ + class Group<TGroupName extends string, TParentGroup extends Group<string> | undefined = any> { + /** @deprecated */ + constructor(router: Router, options?: GroupOptions<TGroupName>, parent?: TParentGroup); - name: string; + /** @deprecated */ + name: TGroupName; + /** @deprecated */ prefix: string; - options: GroupOptions; + /** @deprecated */ + options: GroupOptions<TGroupName>; - parent: Group | undefined; + /** @deprecated */ + parent: TParentGroup; - route(pathDef: string, options: RouteOptions, group?: Group): Route; + /** @deprecated */ + route<TRouteName extends string>(pathDef: string, options: RouteOptions<TRouteName>, group?: TParentGroup): Route<TRouteName, TGroup>; - group(options?: GroupOptions): Group; + /** @deprecated */ + group(options?: GroupOptions<TGroupName>): Group; + /** @deprecated */ callSubscriptions(current: Current): void; } @@ -85,9 +94,9 @@ declare module 'meteor/kadira:flow-router' { path: string; context: Context; params: Record<string, string>; - queryParams: Record<string, string>; - route?: Route | undefined; - oldRoute?: Route | undefined; + queryParams?: Record<string, string>; + route?: Route<string> | undefined; + oldRoute?: Route<string> | undefined; }; type RouterOptions = { @@ -97,9 +106,16 @@ declare module 'meteor/kadira:flow-router' { class Router { constructor(); - route(pathDef: string, options: RouteOptions, group?: Group): Route; + route<TRouteName extends string>(pathDef: string, options: RouteOptions<TRouteName>): Route<TRouteName, undefined>; + + route<TRouteName extends string, TGroup extends Group<string>>( + pathDef: string, + options: RouteOptions<TRouteName>, + group: TGroup, + ): Route<TRouteName, TGroup>; - group(options: GroupOptions): Group; + /** @deprecated */ + group<TGroupName extends string>(options: GroupOptions<TGroupName>): Group<TGroupName>; path(pathDef: string, fields?: Record<string, string>, queryParams?: Record<string, string>): string; @@ -113,7 +129,7 @@ declare module 'meteor/kadira:flow-router' { setParams(newParams: Record<string, string>): boolean; - setQueryParams(newParams: Record<string, string | null>): boolean; + setQueryParams(newParams: Record<string, string | undefined | null>): boolean; current(): Current; @@ -127,7 +143,7 @@ declare module 'meteor/kadira:flow-router' { wait(): void; - notFound: Omit<RouteOptions, 'name'>; + notFound: Omit<RouteOptions<any>, 'name'>; getRouteName(): string; @@ -144,10 +160,18 @@ declare module 'meteor/kadira:flow-router' { _routesMap: Record<string, Route>; _updateCallbacks(): void; + + _current: Current; } const FlowRouter: Router & { Route: typeof Route; Router: typeof Router; + _page: { + start(): void; + stop(): void; + show(path: string): void; + dispatch(ctx: { path: string; params: any }): void; + }; }; } diff --git a/apps/meteor/definition/externals/meteor/meteor.d.ts b/apps/meteor/definition/externals/meteor/meteor.d.ts index 1bf3bf0ebc64..9d7028675669 100644 --- a/apps/meteor/definition/externals/meteor/meteor.d.ts +++ b/apps/meteor/definition/externals/meteor/meteor.d.ts @@ -122,6 +122,8 @@ declare module 'meteor/meteor' { cb: (error?: Error | Meteor.Error | Meteor.TypedError) => void, ): void; + function loginWithSamlToken(token: string, cb: (error?: Error | Meteor.Error | Meteor.TypedError) => void): void; + function methods<TServerMethods extends ServerMethods>(methods: { [TMethodName in keyof TServerMethods]?: ( this: MethodThisType, diff --git a/apps/meteor/ee/app/livechat-enterprise/client/views/livechatSideNavItems.js b/apps/meteor/ee/app/livechat-enterprise/client/views/livechatSideNavItems.js index 7fe3c468f323..a669834d103a 100644 --- a/apps/meteor/ee/app/livechat-enterprise/client/views/livechatSideNavItems.js +++ b/apps/meteor/ee/app/livechat-enterprise/client/views/livechatSideNavItems.js @@ -2,37 +2,37 @@ import { registerOmnichannelSidebarItem } from '../../../../../client/views/omni import { hasPermission, hasAtLeastOnePermission } from '../../../../../app/authorization/client'; registerOmnichannelSidebarItem({ - href: 'omnichannel-monitors', + href: '/omnichannel/monitors', i18nLabel: 'Livechat_Monitors', permissionGranted: () => hasPermission('manage-livechat-monitors'), }); registerOmnichannelSidebarItem({ - href: 'omnichannel/units', + href: '/omnichannel/units', i18nLabel: 'Units', permissionGranted: () => hasPermission('manage-livechat-units'), }); registerOmnichannelSidebarItem({ - href: 'omnichannel-canned-responses', + href: '/omnichannel/canned-responses', i18nLabel: 'Canned_Responses', permissionGranted: () => hasPermission('manage-livechat-canned-responses'), }); registerOmnichannelSidebarItem({ - href: 'omnichannel/tags', + href: '/omnichannel/tags', i18nLabel: 'Tags', permissionGranted: () => hasPermission('manage-livechat-tags'), }); registerOmnichannelSidebarItem({ - href: 'omnichannel/sla-policies', + href: '/omnichannel/sla-policies', i18nLabel: 'SLA_Policies', permissionGranted: () => hasAtLeastOnePermission('manage-livechat-sla'), }); registerOmnichannelSidebarItem({ - href: 'omnichannel/priorities', + href: '/omnichannel/priorities', i18nLabel: 'Priorities', permissionGranted: () => hasAtLeastOnePermission('manage-livechat-priorities'), }); diff --git a/apps/meteor/ee/client/omnichannel/components/contextualBar/CannedResponse/index.tsx b/apps/meteor/ee/client/omnichannel/components/contextualBar/CannedResponse/index.tsx index 5d6eed0e8859..f862a63fdf69 100644 --- a/apps/meteor/ee/client/omnichannel/components/contextualBar/CannedResponse/index.tsx +++ b/apps/meteor/ee/client/omnichannel/components/contextualBar/CannedResponse/index.tsx @@ -1,5 +1,5 @@ import { useDebouncedValue, useLocalStorage, useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useCurrentRoute, useRoute } from '@rocket.chat/ui-contexts'; +import { useSetModal, useRouter } from '@rocket.chat/ui-contexts'; import type { FC, MouseEvent } from 'react'; import React, { memo, useCallback, useMemo, useState } from 'react'; @@ -14,8 +14,7 @@ import CannedResponseList from './CannedResponseList'; export const WrapCannedResponseList: FC<{ tabBar: any }> = ({ tabBar }) => { const room = useRoom(); - const [name] = useCurrentRoute(); - const channelRoute = useRoute(name || ''); + const router = useRouter(); const setModal = useSetModal(); const options = useCannedResponseFilterOptions() as [string, string][]; @@ -37,10 +36,13 @@ export const WrapCannedResponseList: FC<{ tabBar: any }> = ({ tabBar }) => { const onClickItem = useMutableCallback((data) => { const { _id: context } = data; - channelRoute?.push({ - id: room._id, - tab: 'canned-responses', - context, + router.navigate({ + name: router.getRouteName() ?? 'live', + params: { + id: room._id, + tab: 'canned-responses', + context, + }, }); }); diff --git a/apps/meteor/ee/client/omnichannel/routes.ts b/apps/meteor/ee/client/omnichannel/routes.ts index a3d80739a86f..3efe8d6aab22 100644 --- a/apps/meteor/ee/client/omnichannel/routes.ts +++ b/apps/meteor/ee/client/omnichannel/routes.ts @@ -2,6 +2,27 @@ import { lazy } from 'react'; import { registerOmnichannelRoute } from '../../../client/views/omnichannel/routes'; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'omnichannel-monitors': { + pattern: '/omnichannel/monitors'; + pathname: '/omnichannel/monitors'; + }; + 'omnichannel-sla-policies': { + pattern: '/omnichannel/sla-policies/:context?/:id?'; + pathname: `/omnichannel/sla-policies${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-priorities': { + pattern: '/omnichannel/priorities/:context?/:id?'; + pathname: `/omnichannel/priorities${`/${string}` | ''}${`/${string}` | ''}`; + }; + 'omnichannel-canned-responses': { + pattern: '/omnichannel/canned-responses/:context?/:id?'; + pathname: `/omnichannel/canned-responses${`/${string}` | ''}${`/${string}` | ''}`; + }; + } +} + registerOmnichannelRoute('/monitors', { name: 'omnichannel-monitors', component: lazy(() => import('./monitors/MonitorsPageContainer')), diff --git a/apps/meteor/ee/client/startup/audit.tsx b/apps/meteor/ee/client/startup/audit.tsx index e3d687fc846c..0f1e8b59ec25 100644 --- a/apps/meteor/ee/client/startup/audit.tsx +++ b/apps/meteor/ee/client/startup/audit.tsx @@ -1,9 +1,8 @@ -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Tracker } from 'meteor/tracker'; import React, { lazy } from 'react'; import { hasAllPermission } from '../../../app/authorization/client'; import { appLayout } from '../../../client/lib/appLayout'; +import { router } from '../../../client/providers/RouterProvider'; import NotAuthorizedPage from '../../../client/views/notAuthorized/NotAuthorizedPage'; import MainLayout from '../../../client/views/root/MainLayout'; import { onToggledFeature } from '../lib/onToggledFeature'; @@ -11,66 +10,55 @@ import { onToggledFeature } from '../lib/onToggledFeature'; const AuditPage = lazy(() => import('../views/audit/AuditPage')); const AuditLogPage = lazy(() => import('../views/audit/AuditLogPage')); -let auditRoute = FlowRouter.route('/audit', { name: 'audit-home' }); -let auditLogRoute = FlowRouter.route('/audit-log', { name: 'audit-log' }); - -const registerRoutes = () => { - FlowRouter._routes = FlowRouter._routes.filter((r) => r !== auditRoute && r !== auditLogRoute); - if (auditRoute.name) { - delete FlowRouter._routesMap[auditRoute.name]; - } - if (auditLogRoute.name) { - delete FlowRouter._routesMap[auditLogRoute.name]; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'audit-home': { + pathname: '/audit'; + pattern: '/audit/:tab?'; + }; + 'audit-log': { + pathname: '/audit-log'; + pattern: '/audit-log'; + }; } +} - auditRoute = FlowRouter.route('/audit/:tab?', { - name: 'audit-home', - action() { - Tracker.autorun(() => { - const canAudit = hasAllPermission('can-audit'); - - appLayout.render(<MainLayout>{canAudit ? <AuditPage /> : <NotAuthorizedPage />}</MainLayout>); - }); - }, - }); +const PermissionGuard = ({ children, permission }: { children: React.ReactNode; permission: string }) => { + const canView = hasAllPermission(permission); - auditLogRoute = FlowRouter.route('/audit-log', { - name: 'audit-log', - action() { - Tracker.autorun(() => { - const canAuditLog = hasAllPermission('can-audit-log'); - - appLayout.render(<MainLayout>{canAuditLog ? <AuditLogPage /> : <NotAuthorizedPage />}</MainLayout>); - }); - }, - }); - - if (FlowRouter._initialized) { - FlowRouter._updateCallbacks(); - FlowRouter.reload(); - } + return <>{canView ? children : <NotAuthorizedPage />}</>; }; -const unregisterRoutes = () => { - FlowRouter._routes = FlowRouter._routes.filter((r) => r !== auditRoute && r !== auditLogRoute); - if (auditRoute.name) { - delete FlowRouter._routesMap[auditRoute.name]; - } - if (auditLogRoute.name) { - delete FlowRouter._routesMap[auditLogRoute.name]; - } - - if (FlowRouter._initialized) { - FlowRouter._updateCallbacks(); - FlowRouter.reload(); - } -}; +let unregisterAuditRoutes: () => void; onToggledFeature('auditing', { up: () => { - registerRoutes(); + unregisterAuditRoutes = router.defineRoutes([ + { + path: '/audit/:tab?', + id: 'audit-home', + element: appLayout.wrap( + <MainLayout> + <PermissionGuard permission='can-audit'> + <AuditPage /> + </PermissionGuard> + </MainLayout>, + ), + }, + { + path: '/audit-log', + id: 'audit-log', + element: appLayout.wrap( + <MainLayout> + <PermissionGuard permission='can-audit-log'> + <AuditLogPage /> + </PermissionGuard> + </MainLayout>, + ), + }, + ]); }, down: () => { - unregisterRoutes(); + unregisterAuditRoutes(); }, }); diff --git a/apps/meteor/ee/client/startup/deviceManagement.ts b/apps/meteor/ee/client/startup/deviceManagement.ts index bac54f23408e..33f1a99dd767 100644 --- a/apps/meteor/ee/client/startup/deviceManagement.ts +++ b/apps/meteor/ee/client/startup/deviceManagement.ts @@ -5,13 +5,26 @@ import { registerAccountRoute, registerAccountSidebarItem, unregisterSidebarItem import { registerAdminRoute, registerAdminSidebarItem, unregisterAdminSidebarItem } from '../../../client/views/admin'; import { onToggledFeature } from '../lib/onToggledFeature'; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'device-management': { + pathname: `/admin/device-management${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/device-management/:context?/:id?'; + }; + 'manage-devices': { + pathname: '/account/manage-devices'; + pattern: '/account/manage-devices'; + }; + } +} + const [registerAdminRouter, unregisterAdminRouter] = registerAdminRoute('/device-management/:context?/:id?', { name: 'device-management', component: lazy(() => import('../views/admin/deviceManagement/DeviceManagementAdminRoute')), ready: false, }); -const [registerAccountRouter, unregisterAccountRouter] = registerAccountRoute('/manage-devices/', { +const [registerAccountRouter, unregisterAccountRouter] = registerAccountRoute('/manage-devices', { name: 'manage-devices', component: lazy(() => import('../views/account/deviceManagement/DeviceManagementAccountPage')), }); diff --git a/apps/meteor/ee/client/startup/engagementDashboard.ts b/apps/meteor/ee/client/startup/engagementDashboard.ts index b6391495bee1..0bd1d4995169 100644 --- a/apps/meteor/ee/client/startup/engagementDashboard.ts +++ b/apps/meteor/ee/client/startup/engagementDashboard.ts @@ -5,6 +5,15 @@ import { hasAllPermission } from '../../../app/authorization/client'; import { registerAdminRoute, registerAdminSidebarItem, unregisterAdminSidebarItem } from '../../../client/views/admin'; import { onToggledFeature } from '../lib/onToggledFeature'; +declare module '@rocket.chat/ui-contexts' { + interface IRouterPaths { + 'engagement-dashboard': { + pattern: '/admin/engagement-dashboard/:tab?'; + pathname: `/admin/engagement-dashboard${`/${string}` | ''}`; + }; + } +} + const [registerRoute, unregisterRoute] = registerAdminRoute('/engagement-dashboard/:tab?', { name: 'engagement-dashboard', component: lazy(() => import('../views/admin/engagementDashboard/EngagementDashboardRoute')), diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx index 29afa2b4a0f8..5423a7aa10ab 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx @@ -1,4 +1,4 @@ -import { useCurrentRoute, useRoute, usePermission } from '@rocket.chat/ui-contexts'; +import { usePermission, useRouter, useRouteParameter } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect } from 'react'; @@ -11,19 +11,30 @@ const isValidTab = (tab: string | undefined): tab is 'users' | 'messages' | 'cha const EngagementDashboardRoute = (): ReactElement | null => { const canViewEngagementDashboard = usePermission('view-engagement-dashboard'); - const engagementDashboardRoute = useRoute('engagement-dashboard'); - const [routeName, routeParams] = useCurrentRoute(); - const { tab } = routeParams ?? {}; + const router = useRouter(); + const tab = useRouteParameter('tab'); - useEffect(() => { - if (routeName !== 'engagement-dashboard') { - return; - } + useEffect( + () => + router.subscribeToRouteChange(() => { + if (router.getRouteName() !== 'engagement-dashboard') { + return; + } - if (!isValidTab(tab)) { - engagementDashboardRoute.replace({ tab: 'users' }); - } - }, [routeName, engagementDashboardRoute, tab]); + const { tab } = router.getRouteParameters(); + + if (!isValidTab(tab)) { + router.navigate( + { + pattern: '/admin/engagement-dashboard/:tab?', + params: { tab: 'users' }, + }, + { replace: true }, + ); + } + }), + [router], + ); const eventStats = useEndpointAction('POST', '/v1/statistics.telemetry'); @@ -38,7 +49,17 @@ const EngagementDashboardRoute = (): ReactElement | null => { eventStats({ params: [{ eventName: 'updateCounter', settingsId: 'Engagement_Dashboard_Load_Count' }], }); - return <EngagementDashboardPage tab={tab} onSelectTab={(tab): void => engagementDashboardRoute.push({ tab })} />; + return ( + <EngagementDashboardPage + tab={tab} + onSelectTab={(tab) => + router.navigate({ + pattern: '/admin/engagement-dashboard/:tab?', + params: { tab }, + }) + } + /> + ); }; export default EngagementDashboardRoute; diff --git a/apps/meteor/ee/client/views/audit/hooks/useAuditTab.ts b/apps/meteor/ee/client/views/audit/hooks/useAuditTab.ts index 12bb9ae8654c..1045e20ea592 100644 --- a/apps/meteor/ee/client/views/audit/hooks/useAuditTab.ts +++ b/apps/meteor/ee/client/views/audit/hooks/useAuditTab.ts @@ -17,7 +17,7 @@ export const useAuditTab = () => { const tab = useRouteParameter('tab'); const type = useMemo(() => tabToTabMap.get(tab ?? 'rooms') ?? '', [tab]); - const auditRoute = useRoute('/audit/:tab?'); + const auditRoute = useRoute('audit-home'); const setType = useMutableCallback((newType: SetStateAction<IAuditLog['fields']['type']>) => { auditRoute.replace({ tab: typeToTabMap[typeof newType === 'function' ? newType(type) : newType] ?? 'rooms' }); diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index f84b927c89e0..69913b79e3c5 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -204,6 +204,13 @@ type ChainedCallbackSignatures = { room: IOmnichannelRoom; options: { forwardingToDepartment?: { oldDepartmentId: string; transferData: any }; clientAction?: boolean }; }) => (IOmnichannelRoom & { chatQueued: boolean }) | void; + 'roomNameChanged': (room: IRoom) => void; + 'roomTopicChanged': (room: IRoom) => void; + 'roomAnnouncementChanged': (room: IRoom) => void; + 'roomTypeChanged': (room: IRoom) => void; + 'archiveRoom': (room: IRoom) => void; + 'unarchiveRoom': (room: IRoom) => void; + 'roomAvatarChanged': (room: IRoom) => void; }; export type Hook = @@ -217,7 +224,6 @@ export type Hook = | 'afterRoomTopicChange' | 'afterSaveUser' | 'afterValidateNewOAuthUser' - | 'archiveRoom' | 'beforeActivateUser' | 'beforeCreateUser' | 'beforeGetMentions' @@ -242,15 +248,9 @@ export type Hook = | 'onValidateLogin' | 'openBroadcast' | 'renderNotification' - | 'roomAnnouncementChanged' - | 'roomAvatarChanged' - | 'roomNameChanged' - | 'roomTopicChanged' - | 'roomTypeChanged' | 'setReaction' | 'streamMessage' | 'streamNewMessage' - | 'unarchiveRoom' | 'unsetReaction' | 'userAvatarSet' | 'userConfirmationEmailRequested' diff --git a/apps/meteor/lib/rooms/roomTypes/direct.ts b/apps/meteor/lib/rooms/roomTypes/direct.ts index 66b677b0a754..8d8764380196 100644 --- a/apps/meteor/lib/rooms/roomTypes/direct.ts +++ b/apps/meteor/lib/rooms/roomTypes/direct.ts @@ -1,6 +1,15 @@ import type { IRoomTypeConfig } from '../../../definition/IRoomTypeConfig'; import type { RoomCoordinator } from '../coordinator'; +declare module '@rocket.chat/ui-contexts' { + export interface IRouterPaths { + direct: { + pathname: `/direct/:rid${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/direct/:rid/:tab?/:context?'; + }; + } +} + export function getDirectMessageRoomType(_coordinator: RoomCoordinator): IRoomTypeConfig { return { identifier: 'd', diff --git a/apps/meteor/lib/rooms/roomTypes/livechat.ts b/apps/meteor/lib/rooms/roomTypes/livechat.ts index 7c7051cc8e50..b05cdefc07a0 100644 --- a/apps/meteor/lib/rooms/roomTypes/livechat.ts +++ b/apps/meteor/lib/rooms/roomTypes/livechat.ts @@ -1,6 +1,15 @@ import type { IRoomTypeConfig } from '../../../definition/IRoomTypeConfig'; import type { RoomCoordinator } from '../coordinator'; +declare module '@rocket.chat/ui-contexts' { + export interface IRouterPaths { + live: { + pathname: `/live/${string}${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/live/:id/:tab?/:context?'; + }; + } +} + export function getLivechatRoomType(_coordinator: RoomCoordinator): IRoomTypeConfig { return { identifier: 'l', diff --git a/apps/meteor/lib/rooms/roomTypes/private.ts b/apps/meteor/lib/rooms/roomTypes/private.ts index e72aec56bd30..959463e94ae8 100644 --- a/apps/meteor/lib/rooms/roomTypes/private.ts +++ b/apps/meteor/lib/rooms/roomTypes/private.ts @@ -1,6 +1,15 @@ import type { IRoomTypeConfig } from '../../../definition/IRoomTypeConfig'; import type { RoomCoordinator } from '../coordinator'; +declare module '@rocket.chat/ui-contexts' { + export interface IRouterPaths { + group: { + pathname: `/group/${string}${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/group/:name/:tab?/:context?'; + }; + } +} + export function getPrivateRoomType(_coordinator: RoomCoordinator): IRoomTypeConfig { return { identifier: 'p', diff --git a/apps/meteor/lib/rooms/roomTypes/public.ts b/apps/meteor/lib/rooms/roomTypes/public.ts index d09b8aef9971..2f90488d8835 100644 --- a/apps/meteor/lib/rooms/roomTypes/public.ts +++ b/apps/meteor/lib/rooms/roomTypes/public.ts @@ -1,6 +1,15 @@ import type { IRoomTypeConfig } from '../../../definition/IRoomTypeConfig'; import type { RoomCoordinator } from '../coordinator'; +declare module '@rocket.chat/ui-contexts' { + export interface IRouterPaths { + channel: { + pathname: `/channel/${string}${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/channel/:name/:tab?/:context?'; + }; + } +} + export function getPublicRoomType(_coordinator: RoomCoordinator): IRoomTypeConfig { return { identifier: 'c', diff --git a/apps/meteor/lib/rooms/roomTypes/voip.ts b/apps/meteor/lib/rooms/roomTypes/voip.ts index 0c692baa5f45..04ae151ef5a4 100644 --- a/apps/meteor/lib/rooms/roomTypes/voip.ts +++ b/apps/meteor/lib/rooms/roomTypes/voip.ts @@ -1,6 +1,15 @@ import type { IRoomTypeConfig } from '../../../definition/IRoomTypeConfig'; import type { RoomCoordinator } from '../coordinator'; +declare module '@rocket.chat/ui-contexts' { + export interface IRouterPaths { + voip: { + pathname: `/voip/${string}${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/voip/:id/:tab?/:context?'; + }; + } +} + export function getVoipRoomType(_coordinator: RoomCoordinator): IRoomTypeConfig { return { identifier: 'v', diff --git a/apps/meteor/lib/utils/generatePath.ts b/apps/meteor/lib/utils/generatePath.ts new file mode 100644 index 000000000000..40c4bab1043c --- /dev/null +++ b/apps/meteor/lib/utils/generatePath.ts @@ -0,0 +1,13 @@ +import { compile } from 'path-to-regexp'; + +type PathParams<TPath extends string> = TPath extends `${string}:${infer TParam}/${infer TRest}` + ? (TParam extends `${infer U}?` ? U : TParam) | PathParams<TRest> + : TPath extends `${string}:${infer TParam}` + ? TParam extends `${infer U}?` + ? U + : TParam + : never; + +export function generatePath<TPath extends string>(path: TPath, params?: Partial<Record<PathParams<TPath>, string>>): string { + return compile(path, { encode: encodeURIComponent })(params); +} diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 726ddd56e118..4f2422cb3fed 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -39,7 +39,7 @@ "testapi": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.api.js", "testunit": "npm run .testunit:definition && npm run .testunit:client && npm run .testunit:server", ".testunit:server": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.js", - ".testunit:client": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.client.js", + ".testunit:client": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.client.js --exit", ".testunit:definition": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.definition.js", "testunit-watch": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --watch --config ./.mocharc.js", "test": "npm run testapi && npm run testui", diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-center.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-center.spec.ts index b9ac92d67a9a..0f5f1e9605b2 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-center.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-contact-center.spec.ts @@ -9,7 +9,7 @@ import { test, expect } from '../utils/test'; const createContact = (generateToken = false) => ({ id: null, - name: `${faker.person.firstName()} ${faker.name.lastName()}`, + name: `${faker.person.firstName()} ${faker.person.lastName()}`, email: faker.internet.email().toLowerCase(), phone: faker.phone.number('+############'), token: generateToken ? createToken() : null, diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts index 36e2b54601f9..6641baae2e66 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-departaments.spec.ts @@ -78,12 +78,12 @@ test.describe.serial('omnichannel-departments', () => { await poOmnichannelDepartments.btnSave.click(); await poOmnichannelDepartments.btnCloseToastSuccess.click(); - await poOmnichannelDepartments.inputSearch.fill(departmentName); + await poOmnichannelDepartments.search(departmentName); await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); }); await test.step('expect update department name', async () => { - await poOmnichannelDepartments.inputSearch.fill(departmentName); + await poOmnichannelDepartments.search(departmentName); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); @@ -92,14 +92,14 @@ test.describe.serial('omnichannel-departments', () => { await poOmnichannelDepartments.btnSave.click(); await poOmnichannelDepartments.btnCloseToastSuccess.click(); - await poOmnichannelDepartments.inputSearch.fill(`edited-${departmentName}`); + await poOmnichannelDepartments.search(`edited-${departmentName}`); await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); }); await test.step('expect archive department', async () => { await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); - await poOmnichannelDepartments.inputSearch.fill(`edited-${departmentName}`); + await poOmnichannelDepartments.search(`edited-${departmentName}`); await poOmnichannelDepartments.firstRowInTableMenu.click(); @@ -121,7 +121,7 @@ test.describe.serial('omnichannel-departments', () => { await test.step('expect unarchive department', async () => { await poOmnichannelDepartments.archivedDepartmentsTab.click(); - await poOmnichannelDepartments.inputSearch.fill(`edited-${departmentName}`); + await poOmnichannelDepartments.search(`edited-${departmentName}`); await poOmnichannelDepartments.firstRowInTableMenu.click(); @@ -133,7 +133,7 @@ test.describe.serial('omnichannel-departments', () => { await test.step('expect delete department', async () => { await poOmnichannelDepartments.allDepartmentsTab.click(); - await poOmnichannelDepartments.inputSearch.fill(`edited-${departmentName}`); + await poOmnichannelDepartments.search(`edited-${departmentName}`); await poOmnichannelDepartments.selectedDepartmentMenu(`edited-${departmentName}`).click(); @@ -143,7 +143,7 @@ test.describe.serial('omnichannel-departments', () => { await poOmnichannelDepartments.btnModalConfirmDelete.click(); - await poOmnichannelDepartments.inputSearch.fill(`edited-${departmentName}`); + await poOmnichannelDepartments.search(`edited-${departmentName}`); await expect(poOmnichannelDepartments.firstRowInTable).toHaveCount(0); }); @@ -160,12 +160,12 @@ test.describe.serial('omnichannel-departments', () => { await poOmnichannelDepartments.btnSave.click(); await poOmnichannelDepartments.btnCloseToastSuccess.click(); - await poOmnichannelDepartments.inputSearch.fill(tagsDepartmentName); + await poOmnichannelDepartments.search(tagsDepartmentName); await expect(poOmnichannelDepartments.firstRowInTable).toBeVisible(); }); await test.step('expect save form button be disabled', async () => { - await poOmnichannelDepartments.inputSearch.fill(tagsDepartmentName); + await poOmnichannelDepartments.search(tagsDepartmentName); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); await expect(poOmnichannelDepartments.btnSave).toBeDisabled(); @@ -173,7 +173,7 @@ test.describe.serial('omnichannel-departments', () => { }); await test.step('Disabled tags state', async () => { - await poOmnichannelDepartments.inputSearch.fill(tagsDepartmentName); + await poOmnichannelDepartments.search(tagsDepartmentName); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); @@ -188,9 +188,9 @@ test.describe.serial('omnichannel-departments', () => { }); await test.step('Enabled tags state', async () => { - const tagName = faker.datatype.string(5); + const tagName = faker.string.sample(5); - await poOmnichannelDepartments.inputSearch.fill(tagsDepartmentName); + await poOmnichannelDepartments.search(tagsDepartmentName); await poOmnichannelDepartments.firstRowInTableMenu.click(); await poOmnichannelDepartments.menuEditOption.click(); @@ -224,7 +224,7 @@ test.describe.serial('omnichannel-departments', () => { }); await test.step('expect to not be possible adding same tag twice', async () => { - const tagName = faker.datatype.string(5); + const tagName = faker.string.sample(5); await poOmnichannelDepartments.inputTags.fill(tagName); await poOmnichannelDepartments.btnTagsAdd.click(); await poOmnichannelDepartments.inputTags.fill(tagName); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts b/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts index ebca2dc9450f..6b10bcd47b2e 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/omnichannel-sidenav.ts @@ -8,15 +8,15 @@ export class OmnichannelSidenav { } get linkDepartments(): Locator { - return this.page.locator('a[href="omnichannel/departments"]'); + return this.page.locator('a[href="/omnichannel/departments"]'); } get linkAgents(): Locator { - return this.page.locator('a[href="omnichannel/agents"]'); + return this.page.locator('a[href="/omnichannel/agents"]'); } get linkManagers(): Locator { - return this.page.locator('a[href="omnichannel/managers"]'); + return this.page.locator('a[href="/omnichannel/managers"]'); } get linkCustomFields(): Locator { @@ -24,7 +24,7 @@ export class OmnichannelSidenav { } get linkCurrentChats(): Locator { - return this.page.locator('a[href="omnichannel/current"]'); + return this.page.locator('a[href="/omnichannel/current"]'); } get linkTriggers(): Locator { @@ -32,10 +32,10 @@ export class OmnichannelSidenav { } get linkSlaPolicies(): Locator { - return this.page.locator('a[href="omnichannel/sla-policies"]'); + return this.page.locator('a[href="/omnichannel/sla-policies"]'); } get linkPriorities(): Locator { - return this.page.locator('a[href="omnichannel/priorities"]'); + return this.page.locator('a[href="/omnichannel/priorities"]'); } } diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts index 59611bc77f89..347708f312ef 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-departments.ts @@ -16,6 +16,11 @@ export class OmnichannelDepartments { return this.page.locator('[placeholder="Search"]'); } + async search(text: string) { + await this.inputSearch.fill(text); + await this.page.waitForTimeout(500); + } + get btnNew() { return this.page.locator('button.rcx-button >> text="New"'); } diff --git a/apps/meteor/tests/mocks/client/RouterContextMock.tsx b/apps/meteor/tests/mocks/client/RouterContextMock.tsx index 9a403065ffea..c21c05492e3e 100644 --- a/apps/meteor/tests/mocks/client/RouterContextMock.tsx +++ b/apps/meteor/tests/mocks/client/RouterContextMock.tsx @@ -1,72 +1,119 @@ -import type { ReactElement, ReactNode } from 'react'; -import React, { useCallback, useRef, useState, useMemo } from 'react'; +import type { MutableRefObject, ReactElement, ReactNode } from 'react'; +import React, { useRef, useMemo } from 'react'; +import type { To, SearchParameters, LocationPathname, LocationSearch } from '@rocket.chat/ui-contexts'; import { RouterContext } from '@rocket.chat/ui-contexts'; -import { Emitter } from '@rocket.chat/emitter'; +import { compile } from 'path-to-regexp'; -const useSubscribableState = <T,>(initialState: T | (() => T)) => { - const [value, setValue] = useState<T>(initialState); +import type { UpgradeTabVariant } from '../../../lib/upgradeTab'; - const emitterRef = useRef(new Emitter<{ update: void }>()); +const encodeSearchParameters = (searchParameters: SearchParameters) => { + const search = new URLSearchParams(); - const get = useCallback(() => value, [value]); + for (const [key, value] of Object.entries(searchParameters)) { + search.append(key, value); + } - const set = useCallback((newValue: T | ((prev: T) => T)) => { - setValue(newValue); - emitterRef.current.emit('update'); - }, []); + const searchString = search.toString(); - const subscribe = useMemo(() => emitterRef.current.on.bind(emitterRef.current, 'update'), []); - - return { get, set, subscribe }; + return searchString ? (`?${searchString}` as `?${LocationSearch}`) : ''; }; -type RouteTuple = [name: string, parameters?: Record<string, string>, queryStringParameters?: Record<string, string>]; +const buildRoutePath = (to: To): LocationPathname | `${LocationPathname}?${LocationSearch}` => { + if (typeof to === 'string') { + return to; + } + + if ('pathname' in to) { + const { pathname, search = {} } = to; + return (pathname + encodeSearchParameters(search)) as LocationPathname | `${LocationPathname}?${LocationSearch}`; + } + + if ('pattern' in to) { + const { pattern, params = {}, search = {} } = to; + return (compile(pattern, { encode: encodeURIComponent })(params) + encodeSearchParameters(search)) as + | LocationPathname + | `${LocationPathname}?${LocationSearch}`; + } + + if ('name' in to) { + const { name, params = {}, search = {} } = to; + + switch (name) { + case 'audit-home': + return `/audit${encodeSearchParameters(search)}`; + + case 'audit-log': + return `/audit-log${encodeSearchParameters(search)}`; + + case 'marketplace': + return `/marketplace/${params.context}/${params.page}${encodeSearchParameters(search)}`; + + case 'upgrade': + return `/admin/upgrade/${params.type as UpgradeTabVariant}${encodeSearchParameters(search)}`; + } + + return (compile(name, { encode: encodeURIComponent })(params) + encodeSearchParameters(search)) as + | LocationPathname + | `${LocationPathname}?${LocationSearch}`; + } + + throw new Error('Invalid route'); +}; type RouterContextMockProps = { children?: ReactNode; - initialRoute?: RouteTuple; - pushRoute?: (...args: RouteTuple) => void; - replaceRoute?: (...args: RouteTuple) => void; + navigate?: (toOrDelta: number | To) => void; + currentPath?: MutableRefObject<string | undefined>; }; -const RouterContextMock = ({ children, initialRoute, pushRoute, replaceRoute }: RouterContextMockProps): ReactElement => { - const currentRoute = useSubscribableState(initialRoute ?? (['home'] as RouteTuple)); +const RouterContextMock = ({ children, navigate, currentPath }: RouterContextMockProps): ReactElement => { + const history = useRef<{ stack: To[]; index: number }>({ stack: ['/'], index: 0 }); + + if (currentPath) { + currentPath.current = buildRoutePath(history.current.stack[history.current.index]); + } return ( <RouterContext.Provider value={useMemo(() => { - const subscribeToCurrentRoute = currentRoute.subscribe; - const getCurrentRoute = currentRoute.get; - const setCurrentRoute = currentRoute.set; - return { - queryRoutePath: () => [() => (): void => undefined, (): undefined => undefined], - queryRouteUrl: () => [() => (): void => undefined, (): undefined => undefined], - pushRoute: ( - name: string, - parameters?: Record<string, string>, - queryStringParameters?: ((prev: Record<string, string>) => Record<string, string>) | Record<string, string>, - ) => { - const queryParams = typeof queryStringParameters === 'function' ? queryStringParameters({}) : queryStringParameters; - setCurrentRoute([name, parameters, queryParams]); - pushRoute?.(name, parameters, queryParams); - }, - replaceRoute: ( - name: string, - parameters?: Record<string, string>, - queryStringParameters?: ((prev: Record<string, string>) => Record<string, string>) | Record<string, string>, - ) => { - const queryParams = typeof queryStringParameters === 'function' ? queryStringParameters({}) : queryStringParameters; - setCurrentRoute([name, parameters, queryParams]); - replaceRoute?.(name, parameters, queryParams); - }, - queryRouteParameter: () => [() => (): void => undefined, (): undefined => undefined], - queryQueryStringParameter: () => [() => (): void => undefined, (): undefined => undefined], - queryCurrentRoute: () => [subscribeToCurrentRoute, getCurrentRoute], - setQueryString: () => undefined, - getRoutePath: () => '/', + subscribeToRouteChange: () => () => undefined, + getLocationPathname: () => '/', + getLocationSearch: () => '', + getRouteParameters: () => ({}), + getSearchParameters: () => ({}), + getRouteName: () => 'home', + buildRoutePath, + navigate: + navigate ?? + ((toOrDelta: number | To) => { + if (typeof toOrDelta === 'number') { + history.current.index += toOrDelta; + + if (history.current.index < 0) { + history.current.index = 0; + } + + if (history.current.index >= history.current.stack.length) { + history.current.index = history.current.stack.length - 1; + } + + return; + } + + history.current.stack = history.current.stack.slice(0, history.current.index + 1); + history.current.stack.push(toOrDelta); + history.current.index = history.current.stack.length - 1; + + if (currentPath) { + currentPath.current = buildRoutePath(history.current.stack[history.current.index]); + } + }), + defineRoutes: () => () => undefined, + getRoutes: () => [], + subscribeToRoutesChange: () => () => undefined, }; - }, [currentRoute.get, currentRoute.set, currentRoute.subscribe, pushRoute, replaceRoute])} + }, [currentPath, navigate])} > {children} </RouterContext.Provider> diff --git a/apps/meteor/tests/unit/client/views/notFound/NotFoundPage.spec.tsx b/apps/meteor/tests/unit/client/views/notFound/NotFoundPage.spec.tsx index 8e3c97f0a164..cf08d8562bbb 100644 --- a/apps/meteor/tests/unit/client/views/notFound/NotFoundPage.spec.tsx +++ b/apps/meteor/tests/unit/client/views/notFound/NotFoundPage.spec.tsx @@ -1,6 +1,7 @@ import { render, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { expect, spy } from 'chai'; +import { expect } from 'chai'; +import type { MutableRefObject } from 'react'; import React from 'react'; import RouterContextMock from '../../../../mocks/client/RouterContextMock'; @@ -28,16 +29,18 @@ describe('views/notFound/NotFoundPage', () => { context('"Return to home" button', () => { context('when clicked', () => { it('should go back on history', async () => { - const pushRoute = spy(); + const currentPath: MutableRefObject<string | undefined> = { current: undefined }; + render( - <RouterContextMock pushRoute={pushRoute}> + <RouterContextMock currentPath={currentPath}> <NotFoundPage /> </RouterContextMock>, ); const button = screen.getByRole('button', { name: 'Homepage' }); userEvent.click(button); - await waitFor(() => expect(pushRoute).to.have.been.called.with('home')); + + await waitFor(() => expect(currentPath.current).to.be.equal('/home')); }); }); }); diff --git a/apps/meteor/tsconfig.json b/apps/meteor/tsconfig.json index 1adc6cac5adf..e07772b269a9 100644 --- a/apps/meteor/tsconfig.json +++ b/apps/meteor/tsconfig.json @@ -23,7 +23,7 @@ /* Support absolute /imports/* with a leading '/' */ "/*": ["*"], "meteor/*": [ - "node_modules/@types/meteor/*", + "../../node_modules/@types/meteor/*", ".meteor/local/types/packages.d.ts" ] }, diff --git a/packages/ui-contexts/.eslintrc.json b/packages/ui-contexts/.eslintrc.json index a83aeda48e66..5b4e017cb820 100644 --- a/packages/ui-contexts/.eslintrc.json +++ b/packages/ui-contexts/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "plugin:react-hooks/recommended"], "ignorePatterns": ["**/dist"] } diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index ab5c03750ab4..2fac15a34858 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -12,6 +12,7 @@ "@types/react-dom": "~17.0.20", "@types/use-sync-external-store": "^0.0.3", "eslint": "~8.43.0", + "eslint-plugin-react-hooks": "^4.6.0", "jest": "~29.5.0", "mongodb": "^4.12.1", "react": "~17.0.2", diff --git a/packages/ui-contexts/src/RouterContext.ts b/packages/ui-contexts/src/RouterContext.ts index 697c41ef6995..3fbd4bd98cf2 100644 --- a/packages/ui-contexts/src/RouterContext.ts +++ b/packages/ui-contexts/src/RouterContext.ts @@ -1,60 +1,96 @@ +import type { ReactNode } from 'react'; import { createContext } from 'react'; -export type RouteName = string; +export interface IRouterPaths { + index: { + pattern: '/'; + pathname: '/'; + }; + home: { + pattern: '/home'; + pathname: '/home'; + }; +} + +export type LocationPathname = IRouterPaths[keyof IRouterPaths]['pathname']; +export type LocationSearch = string; export type RouteParameters = Record<string, string>; +export type SearchParameters = Record<string, string>; + +export type RouteName = keyof IRouterPaths; +export type RouterPathPattern = IRouterPaths[keyof IRouterPaths]['pattern']; -export type QueryStringParameters = Record<string, string>; +export type To = + | LocationPathname + | { + name: RouteName; + params?: RouteParameters; + search?: SearchParameters; + } + | { + pattern: RouterPathPattern; + params?: RouteParameters; + search?: SearchParameters; + } + | { + pathname: LocationPathname; + search?: SearchParameters; + }; -export type RouteGroupName = string; +type RelativeRoutingType = 'route' | 'path'; + +export type RouteObject = + | { + path: RouterPathPattern; + id: RouteName; + element: ReactNode; + } + | { + path: '*'; + id: 'not-found'; + element: ReactNode; + }; export type RouterContextValue = { - queryRoutePath: ( - name: RouteName, - parameters: RouteParameters | undefined, - queryStringParameters: QueryStringParameters | undefined, - ) => [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => string | undefined]; - queryRouteUrl: ( - name: RouteName, - parameters: RouteParameters | undefined, - queryStringParameters: QueryStringParameters | undefined, - ) => [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => string | undefined]; - pushRoute: ( - name: RouteName, - parameters: RouteParameters | undefined, - queryStringParameters?: ((prev: Record<string, string>) => Record<string, string>) | Record<string, string>, - ) => void; - replaceRoute: ( - name: RouteName, - parameters: RouteParameters | undefined, - queryStringParameters?: ((prev: Record<string, string>) => Record<string, string>) | Record<string, string>, - ) => void; - queryRouteParameter: (name: string) => [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => string | undefined]; - queryQueryStringParameter: ( - name: string, - ) => [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => string | undefined]; - queryCurrentRoute: () => [ - subscribe: (onStoreChange: () => void) => () => void, - getSnapshot: () => [RouteName?, RouteParameters?, QueryStringParameters?, RouteGroupName?], - ]; - setQueryString(parameters: Record<string, string | null>): void; - setQueryString(fn: (parameters: Record<string, string>) => Record<string, string>): void; - getRoutePath(nameOrPathDef: string, parameters?: Record<string, string>, queryStringParameters?: Record<string, string>): string; + subscribeToRouteChange(onRouteChange: () => void): () => void; + getLocationPathname(): LocationPathname; + getLocationSearch(): LocationSearch; + getRouteParameters(): RouteParameters; + getSearchParameters(): SearchParameters; + getRouteName(): RouteName | undefined; + buildRoutePath(to: To): LocationPathname | `${LocationPathname}?${LocationSearch}`; + navigate(to: To, options?: { replace?: boolean; state?: any; relative?: RelativeRoutingType }): void; + navigate(delta: number): void; + defineRoutes(routes: RouteObject[]): () => void; + getRoutes(): RouteObject[]; + subscribeToRoutesChange(onRoutesChange: () => void): () => void; }; export const RouterContext = createContext<RouterContextValue>({ - queryRoutePath: () => [() => (): void => undefined, (): undefined => undefined], - queryRouteUrl: () => [() => (): void => undefined, (): undefined => undefined], - pushRoute: () => undefined, - replaceRoute: () => undefined, - queryRouteParameter: () => [() => (): void => undefined, (): undefined => undefined], - queryQueryStringParameter: () => [() => (): void => undefined, (): undefined => undefined], - queryCurrentRoute: () => [ - () => (): void => undefined, - (): [undefined, RouteParameters, QueryStringParameters, undefined] => [undefined, {}, {}, undefined], - ], - setQueryString: () => undefined, - getRoutePath: () => { + subscribeToRouteChange: () => () => undefined, + getLocationPathname: () => { + throw new Error('not implemented'); + }, + getRouteParameters: () => { + throw new Error('not implemented'); + }, + getLocationSearch: () => { + throw new Error('not implemented'); + }, + getSearchParameters: () => { + throw new Error('not implemented'); + }, + getRouteName: () => { + throw new Error('not implemented'); + }, + buildRoutePath: () => { + throw new Error('not implemented'); + }, + navigate: () => undefined, + defineRoutes: () => () => undefined, + getRoutes: () => { throw new Error('not implemented'); }, + subscribeToRoutesChange: () => () => undefined, }); diff --git a/packages/ui-contexts/src/hooks/useCurrentRoute.ts b/packages/ui-contexts/src/hooks/useCurrentRoute.ts deleted file mode 100644 index 99ea13c66b91..000000000000 --- a/packages/ui-contexts/src/hooks/useCurrentRoute.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useContext, useMemo } from 'react'; -import { useSyncExternalStore } from 'use-sync-external-store/shim'; - -import type { QueryStringParameters, RouteGroupName, RouteName, RouteParameters } from '../RouterContext'; -import { RouterContext } from '../RouterContext'; - -export const useCurrentRoute = (): [RouteName?, RouteParameters?, QueryStringParameters?, RouteGroupName?] => { - const { queryCurrentRoute } = useContext(RouterContext); - - const [subscribe, getSnapshot] = useMemo(() => queryCurrentRoute(), [queryCurrentRoute]); - return useSyncExternalStore(subscribe, getSnapshot); -}; diff --git a/packages/ui-contexts/src/hooks/useCurrentRoutePath.ts b/packages/ui-contexts/src/hooks/useCurrentRoutePath.ts new file mode 100644 index 000000000000..dd7ba683bea1 --- /dev/null +++ b/packages/ui-contexts/src/hooks/useCurrentRoutePath.ts @@ -0,0 +1,21 @@ +import { useCallback, useContext } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; + +import { RouterContext } from '../RouterContext'; + +export const useCurrentRoutePath = () => { + const router = useContext(RouterContext); + + const getSnapshot = useCallback(() => { + const name = router.getRouteName(); + return name + ? router.buildRoutePath({ + name, + params: router.getRouteParameters(), + search: router.getSearchParameters(), + }) + : undefined; + }, [router]); + + return useSyncExternalStore(router.subscribeToRouteChange, getSnapshot); +}; diff --git a/packages/ui-contexts/src/hooks/useLogout.ts b/packages/ui-contexts/src/hooks/useLogout.ts index 9e67bbfc3a86..fbbe758d48c4 100644 --- a/packages/ui-contexts/src/hooks/useLogout.ts +++ b/packages/ui-contexts/src/hooks/useLogout.ts @@ -1,16 +1,16 @@ import { useContext } from 'react'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useRoute } from './useRoute'; import { UserContext } from '../UserContext'; +import { useRouter } from './useRouter'; export const useLogout = (): (() => void) => { - const router = useRoute('home'); + const router = useRouter(); const { logout } = useContext(UserContext); const handleLogout = useMutableCallback(() => { logout(); - router.push({}); + router.navigate('/'); }); return handleLogout; diff --git a/packages/ui-contexts/src/hooks/useQueryStringParameter.ts b/packages/ui-contexts/src/hooks/useQueryStringParameter.ts deleted file mode 100644 index cc7a50dfad70..000000000000 --- a/packages/ui-contexts/src/hooks/useQueryStringParameter.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useContext, useMemo } from 'react'; -import { useSyncExternalStore } from 'use-sync-external-store/shim'; - -import { RouterContext } from '../RouterContext'; - -export const useQueryStringParameter = (name: string): string | undefined => { - const { queryQueryStringParameter } = useContext(RouterContext); - - const [subscribe, getSnapshot] = useMemo(() => queryQueryStringParameter(name), [queryQueryStringParameter, name]); - - return useSyncExternalStore(subscribe, getSnapshot); -}; diff --git a/packages/ui-contexts/src/hooks/useRoute.ts b/packages/ui-contexts/src/hooks/useRoute.ts index 9123bd01f5c0..50fbbc3ca769 100644 --- a/packages/ui-contexts/src/hooks/useRoute.ts +++ b/packages/ui-contexts/src/hooks/useRoute.ts @@ -1,11 +1,9 @@ import { useContext, useMemo } from 'react'; -import type { QueryStringParameters, RouteParameters } from '../RouterContext'; +import type { RouteName, RouteParameters } from '../RouterContext'; import { RouterContext } from '../RouterContext'; type Route = { - getPath: (parameters?: RouteParameters, queryStringParameters?: QueryStringParameters) => string | undefined; - getUrl: (parameters?: RouteParameters, queryStringParameters?: QueryStringParameters) => string | undefined; push: ( parameters?: RouteParameters, queryStringParameters?: ((prev: Record<string, string>) => Record<string, string>) | Record<string, string>, @@ -16,16 +14,23 @@ type Route = { ) => void; }; -export const useRoute = (name: string): Route => { - const { queryRoutePath, queryRouteUrl, pushRoute, replaceRoute } = useContext(RouterContext); +/** @deprecated prefer `useRouter` */ +export const useRoute = (name: RouteName): Route => { + const router = useContext(RouterContext); return useMemo<Route>( () => ({ - getPath: (parameters, queryStringParameters) => queryRoutePath(name, parameters, queryStringParameters)[1](), - getUrl: (parameters, queryStringParameters) => queryRouteUrl(name, parameters, queryStringParameters)[1](), - push: (parameters, queryStringParameters) => pushRoute(name, parameters, queryStringParameters), - replace: (parameters, queryStringParameters) => replaceRoute(name, parameters, queryStringParameters), + push: (params, queryStringParameters) => { + const search = + typeof queryStringParameters === 'function' ? queryStringParameters(router.getSearchParameters()) : queryStringParameters; + router.navigate({ name, params, search }, { replace: false }); + }, + replace: (params, queryStringParameters) => { + const search = + typeof queryStringParameters === 'function' ? queryStringParameters(router.getSearchParameters()) : queryStringParameters; + router.navigate({ name, params, search }, { replace: true }); + }, }), - [queryRoutePath, queryRouteUrl, name, pushRoute, replaceRoute], + [name, router], ); }; diff --git a/packages/ui-contexts/src/hooks/useRouteParameter.ts b/packages/ui-contexts/src/hooks/useRouteParameter.ts index eaae7e45fd06..3990c516246a 100644 --- a/packages/ui-contexts/src/hooks/useRouteParameter.ts +++ b/packages/ui-contexts/src/hooks/useRouteParameter.ts @@ -1,12 +1,14 @@ -import { useContext, useMemo } from 'react'; +import { useCallback, useContext } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; import { RouterContext } from '../RouterContext'; export const useRouteParameter = (name: string): string | undefined => { - const { queryRouteParameter } = useContext(RouterContext); + const router = useContext(RouterContext); - const [subscribe, getSnapshot] = useMemo(() => queryRouteParameter(name), [queryRouteParameter, name]); + const getSnapshot = useCallback(() => { + return router.getRouteParameters()[name]; + }, [router, name]); - return useSyncExternalStore(subscribe, getSnapshot); + return useSyncExternalStore(router.subscribeToRouteChange, getSnapshot); }; diff --git a/packages/ui-contexts/src/hooks/useRoutePath.ts b/packages/ui-contexts/src/hooks/useRoutePath.ts deleted file mode 100644 index 2bbe1ce45d22..000000000000 --- a/packages/ui-contexts/src/hooks/useRoutePath.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useContext, useMemo } from 'react'; -import { useSyncExternalStore } from 'use-sync-external-store/shim'; - -import type { QueryStringParameters, RouteParameters } from '../RouterContext'; -import { RouterContext } from '../RouterContext'; - -export const useRoutePath = ( - name: string, - parameters?: RouteParameters, - queryStringParameters?: QueryStringParameters, -): string | undefined => { - const { queryRoutePath } = useContext(RouterContext); - - const [subscribe, getSnapshot] = useMemo( - () => queryRoutePath(name, parameters, queryStringParameters), - [queryRoutePath, name, parameters, queryStringParameters], - ); - - return useSyncExternalStore(subscribe, getSnapshot); -}; diff --git a/packages/ui-contexts/src/hooks/useRouteUrl.ts b/packages/ui-contexts/src/hooks/useRouteUrl.ts deleted file mode 100644 index f067f776ddf6..000000000000 --- a/packages/ui-contexts/src/hooks/useRouteUrl.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useContext, useMemo } from 'react'; -import { useSyncExternalStore } from 'use-sync-external-store/shim'; - -import type { QueryStringParameters, RouteParameters } from '../RouterContext'; -import { RouterContext } from '../RouterContext'; - -export const useRouteUrl = ( - name: string, - parameters?: RouteParameters, - queryStringParameters?: QueryStringParameters, -): string | undefined => { - const { queryRouteUrl } = useContext(RouterContext); - - const [subscribe, getSnapshot] = useMemo( - () => queryRouteUrl(name, parameters, queryStringParameters), - [queryRouteUrl, name, parameters, queryStringParameters], - ); - - return useSyncExternalStore(subscribe, getSnapshot); -}; diff --git a/packages/ui-contexts/src/hooks/useRouter.ts b/packages/ui-contexts/src/hooks/useRouter.ts new file mode 100644 index 000000000000..314e7682bbf8 --- /dev/null +++ b/packages/ui-contexts/src/hooks/useRouter.ts @@ -0,0 +1,5 @@ +import { useContext } from 'react'; + +import { RouterContext } from '../RouterContext'; + +export const useRouter = () => useContext(RouterContext); diff --git a/packages/ui-contexts/src/hooks/useSearchParameter.ts b/packages/ui-contexts/src/hooks/useSearchParameter.ts new file mode 100644 index 000000000000..98740b249175 --- /dev/null +++ b/packages/ui-contexts/src/hooks/useSearchParameter.ts @@ -0,0 +1,15 @@ +import { useCallback, useContext } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; + +import { RouterContext } from '../RouterContext'; + +export const useSearchParameter = (name: string): string | undefined => { + const { getSearchParameters, subscribeToRouteChange } = useContext(RouterContext); + + const getSnapshot = useCallback(() => { + const searchParameters = getSearchParameters(); + return searchParameters[name]; + }, [getSearchParameters]); + + return useSyncExternalStore(subscribeToRouteChange, getSnapshot); +}; diff --git a/packages/ui-contexts/src/hooks/useSearchParameters.ts b/packages/ui-contexts/src/hooks/useSearchParameters.ts new file mode 100644 index 000000000000..385ddce2529a --- /dev/null +++ b/packages/ui-contexts/src/hooks/useSearchParameters.ts @@ -0,0 +1,9 @@ +import { useContext } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; + +import { RouterContext } from '../RouterContext'; + +export const useSearchParameters = () => { + const { getSearchParameters, subscribeToRouteChange } = useContext(RouterContext); + return useSyncExternalStore(subscribeToRouteChange, getSearchParameters); +}; diff --git a/packages/ui-contexts/src/index.ts b/packages/ui-contexts/src/index.ts index 7780e6fa7fa1..ec981344ed29 100644 --- a/packages/ui-contexts/src/index.ts +++ b/packages/ui-contexts/src/index.ts @@ -5,7 +5,7 @@ export { ConnectionStatusContext, ConnectionStatusContextValue } from './Connect export { CustomSoundContext, CustomSoundContextValue } from './CustomSoundContext'; export { LayoutContext, LayoutContextValue } from './LayoutContext'; export { ModalContext, ModalContextValue } from './ModalContext'; -export { RouterContext, RouterContextValue } from './RouterContext'; +export * from './RouterContext'; export { ServerContext, ServerContextValue } from './ServerContext'; export { SessionContext, SessionContextValue } from './SessionContext'; export { SettingsContext, SettingsContextValue, SettingsContextQuery } from './SettingsContext'; @@ -25,7 +25,7 @@ export { useAttachmentDimensions } from './hooks/useAttachmentDimensions'; export { useAttachmentIsCollapsedByDefault } from './hooks/useAttachmentIsCollapsedByDefault'; export { useConnectionStatus } from './hooks/useConnectionStatus'; export { useCurrentModal } from './hooks/useCurrentModal'; -export { useCurrentRoute } from './hooks/useCurrentRoute'; +export { useCurrentRoutePath } from './hooks/useCurrentRoutePath'; export { useCustomSound } from './hooks/useCustomSound'; export { useEndpoint } from './hooks/useEndpoint'; export type { EndpointFunction } from './hooks/useEndpoint'; @@ -48,14 +48,14 @@ export { useMethod } from './hooks/useMethod'; export { useModal } from './hooks/useModal'; export { usePermission } from './hooks/usePermission'; export { usePermissionWithScopedRoles } from './hooks/usePermissionWithScopedRoles'; -export { useQueryStringParameter } from './hooks/useQueryStringParameter'; export { useRole } from './hooks/useRole'; export { useRolesDescription } from './hooks/useRolesDescription'; export { useRoomAvatarPath } from './hooks/useRoomAvatarPath'; +export { useRouter } from './hooks/useRouter'; export { useRoute } from './hooks/useRoute'; export { useRouteParameter } from './hooks/useRouteParameter'; -export { useRoutePath } from './hooks/useRoutePath'; -export { useRouteUrl } from './hooks/useRouteUrl'; +export { useSearchParameter } from './hooks/useSearchParameter'; +export { useSearchParameters } from './hooks/useSearchParameters'; export { useServerInformation } from './hooks/useServerInformation'; export { useSession } from './hooks/useSession'; export { useSessionDispatch } from './hooks/useSessionDispatch'; diff --git a/packages/web-ui-registration/src/ResetPassword/ResetPasswordPage.tsx b/packages/web-ui-registration/src/ResetPassword/ResetPasswordPage.tsx index c392f1376ac8..4cbe78cb3f76 100644 --- a/packages/web-ui-registration/src/ResetPassword/ResetPasswordPage.tsx +++ b/packages/web-ui-registration/src/ResetPassword/ResetPasswordPage.tsx @@ -3,8 +3,8 @@ import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useSetting, useVerifyPassword, + useRouter, useRouteParameter, - useRoute, useUser, useMethod, useTranslation, @@ -31,7 +31,7 @@ const ResetPasswordPage = (): ReactElement => { const requiresPasswordConfirmation = useSetting('Accounts_RequirePasswordConfirmation'); - const homeRouter = useRoute('home'); + const router = useRouter(); const changePasswordReason = getChangePasswordReason(user || {}); @@ -58,7 +58,7 @@ const ResetPasswordPage = (): ReactElement => { if (token) { const result = await resetPassword(token, data.password); await loginWithToken(result.token); - homeRouter.push({}); + router.navigate('/home'); } else { await setUserPassword(data.password); } diff --git a/yarn.lock b/yarn.lock index 8cc8eada135c..3ffbb18d891f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10886,6 +10886,7 @@ __metadata: "@types/react-dom": ~17.0.20 "@types/use-sync-external-store": ^0.0.3 eslint: ~8.43.0 + eslint-plugin-react-hooks: ^4.6.0 jest: ~29.5.0 mongodb: ^4.12.1 react: ~17.0.2 From b837cb9f2a00979934861818e3f07fe357dc9b70 Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Wed, 5 Jul 2023 13:52:08 -0600 Subject: [PATCH 073/149] fix: Remove the association between BH and disabled/archived departments (#29543) Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .changeset/funny-coins-trade.md | 6 + .changeset/rotten-spoons-teach.md | 6 + .../imports/server/rest/departments.ts | 6 +- .../business-hour/AbstractBusinessHour.ts | 4 +- .../business-hour/BusinessHourManager.ts | 60 ++- .../livechat/server/business-hour/Single.ts | 8 + .../app/livechat/server/lib/Livechat.js | 8 +- .../AutoCompleteDepartmentMultiple.tsx | 6 +- .../server/business-hour/Helper.ts | 18 +- .../server/business-hour/Multiple.ts | 104 +++- .../server/business-hour/lib/business-hour.ts | 19 +- .../server/lib/LivechatEnterprise.ts | 8 +- .../app/livechat-enterprise/server/startup.ts | 2 +- .../additionalForms/BusinessHoursMultiple.js | 2 +- apps/meteor/lib/callbacks.ts | 2 + .../models/raw/LivechatBusinessHours.ts | 4 + .../server/models/raw/LivechatDepartment.ts | 78 ++- .../models/raw/LivechatDepartmentAgents.ts | 10 +- .../tests/data/livechat/business-hours.ts | 12 - .../tests/data/livechat/businessHours.ts | 76 ++- apps/meteor/tests/data/livechat/department.ts | 68 ++- apps/meteor/tests/data/livechat/rooms.ts | 16 - .../end-to-end/api/livechat/10-departments.ts | 3 +- .../api/livechat/19-business-hours.ts | 466 +++++++++++++++++- .../src/models/ILivechatBusinessHoursModel.ts | 2 + .../models/ILivechatDepartmentAgentsModel.ts | 2 + .../src/models/ILivechatDepartmentModel.ts | 9 + 27 files changed, 908 insertions(+), 97 deletions(-) create mode 100644 .changeset/funny-coins-trade.md create mode 100644 .changeset/rotten-spoons-teach.md delete mode 100644 apps/meteor/tests/data/livechat/business-hours.ts diff --git a/.changeset/funny-coins-trade.md b/.changeset/funny-coins-trade.md new file mode 100644 index 000000000000..e7fffad5d960 --- /dev/null +++ b/.changeset/funny-coins-trade.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +Fixed a problem where disabled department agent's where still being activated when applicable business hours met. diff --git a/.changeset/rotten-spoons-teach.md b/.changeset/rotten-spoons-teach.md new file mode 100644 index 000000000000..bd046064eb13 --- /dev/null +++ b/.changeset/rotten-spoons-teach.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +Fixed logic around Default Business Hours where agents from disabled/archived departments where being omitted from processing at closing time diff --git a/apps/meteor/app/livechat/imports/server/rest/departments.ts b/apps/meteor/app/livechat/imports/server/rest/departments.ts index f05b8026bf53..4f7886f83792 100644 --- a/apps/meteor/app/livechat/imports/server/rest/departments.ts +++ b/apps/meteor/app/livechat/imports/server/rest/departments.ts @@ -192,11 +192,9 @@ API.v1.addRoute( }, { async post() { - if (await Livechat.archiveDepartment(this.urlParams._id)) { - return API.v1.success(); - } + await Livechat.archiveDepartment(this.urlParams._id); - return API.v1.failure(); + return API.v1.success(); }, }, ); diff --git a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts index 80cdc5aea296..aeaf6bc1f287 100644 --- a/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts +++ b/apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts @@ -16,7 +16,9 @@ export interface IBusinessHourBehavior { onDisableBusinessHours(): Promise<void>; onAddAgentToDepartment(options?: { departmentId: string; agentsId: string[] }): Promise<any>; onRemoveAgentFromDepartment(options?: Record<string, any>): Promise<any>; - onRemoveDepartment(department?: ILivechatDepartment): Promise<any>; + onRemoveDepartment(options: { department: ILivechatDepartment; agentsIds: string[] }): Promise<any>; + onDepartmentDisabled(department?: ILivechatDepartment): Promise<any>; + onDepartmentArchived(department: Pick<ILivechatDepartment, '_id'>): Promise<void>; onStartBusinessHours(): Promise<void>; afterSaveBusinessHours(businessHourData: ILivechatBusinessHour): Promise<void>; allowAgentChangeServiceStatus(agentId: string): Promise<boolean>; diff --git a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts index 7e72a1756358..6b14f82856a0 100644 --- a/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts +++ b/apps/meteor/app/livechat/server/business-hour/BusinessHourManager.ts @@ -2,11 +2,12 @@ import moment from 'moment'; import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; import type { AgendaCronJobs } from '@rocket.chat/cron'; -import { Users } from '@rocket.chat/models'; +import { LivechatDepartment, Users } from '@rocket.chat/models'; import type { IBusinessHourBehavior, IBusinessHourType } from './AbstractBusinessHour'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; +import { closeBusinessHour } from '../../../../ee/app/livechat-enterprise/server/business-hour/Helper'; import { businessHourLogger } from '../lib/logger'; export class BusinessHourManager { @@ -28,6 +29,7 @@ export class BusinessHourManager { await this.createCronJobsForWorkHours(); businessHourLogger.debug('Cron jobs created, setting up callbacks'); this.setupCallbacks(); + await this.cleanupDisabledDepartmentReferences(); await this.behavior.onStartBusinessHours(); } @@ -38,6 +40,40 @@ export class BusinessHourManager { await this.behavior.onDisableBusinessHours(); } + async restartManager(): Promise<void> { + await this.stopManager(); + await this.startManager(); + } + + async cleanupDisabledDepartmentReferences(): Promise<void> { + // Get business hours with departments enabled and disabled + const bhWithDepartments = await LivechatDepartment.getBusinessHoursWithDepartmentStatuses(); + + if (!bhWithDepartments.length) { + // If there are no bh, skip + return; + } + + for await (const { _id: businessHourId, validDepartments, invalidDepartments } of bhWithDepartments) { + if (!invalidDepartments.length) { + continue; + } + + // If there are no enabled departments, close the business hour + const allDepsAreDisabled = validDepartments.length === 0 && invalidDepartments.length > 0; + if (allDepsAreDisabled) { + const businessHour = await this.getBusinessHour(businessHourId, LivechatBusinessHourTypes.CUSTOM); + if (!businessHour) { + continue; + } + await closeBusinessHour(businessHour); + } + + // Remove business hour from disabled departments + await LivechatDepartment.removeBusinessHourFromDepartmentsByIdsAndBusinessHourId(invalidDepartments, businessHourId); + } + } + async allowAgentChangeServiceStatus(agentId: string): Promise<boolean> { if (!settings.get('Livechat_enable_business_hours')) { return true; @@ -88,6 +124,14 @@ export class BusinessHourManager { return Users.setLivechatStatusActiveBasedOnBusinessHours(agentId); } + async restartCronJobsIfNecessary(): Promise<void> { + if (!settings.get('Livechat_enable_business_hours')) { + return; + } + + await this.createCronJobsForWorkHours(); + } + private setupCallbacks(): void { callbacks.add( 'livechat.removeAgentDepartment', @@ -107,6 +151,18 @@ export class BusinessHourManager { callbacks.priority.HIGH, 'business-hour-livechat-on-save-agent-department', ); + callbacks.add( + 'livechat.afterDepartmentDisabled', + this.behavior.onDepartmentDisabled.bind(this), + callbacks.priority.HIGH, + 'business-hour-livechat-on-department-disabled', + ); + callbacks.add( + 'livechat.afterDepartmentArchived', + this.behavior.onDepartmentArchived.bind(this), + callbacks.priority.HIGH, + 'business-hour-livechat-on-department-archived', + ); callbacks.add( 'livechat.onNewAgentCreated', this.behavior.onNewAgentCreated.bind(this), @@ -119,6 +175,8 @@ export class BusinessHourManager { callbacks.remove('livechat.removeAgentDepartment', 'business-hour-livechat-on-remove-agent-department'); callbacks.remove('livechat.afterRemoveDepartment', 'business-hour-livechat-after-remove-department'); callbacks.remove('livechat.saveAgentDepartment', 'business-hour-livechat-on-save-agent-department'); + callbacks.remove('livechat.afterDepartmentDisabled', 'business-hour-livechat-on-department-disabled'); + callbacks.remove('livechat.afterDepartmentArchived', 'business-hour-livechat-on-department-archived'); callbacks.remove('livechat.onNewAgentCreated', 'business-hour-livechat-on-agent-created'); } diff --git a/apps/meteor/app/livechat/server/business-hour/Single.ts b/apps/meteor/app/livechat/server/business-hour/Single.ts index 2996d5f1c2c7..b351c480ab28 100644 --- a/apps/meteor/app/livechat/server/business-hour/Single.ts +++ b/apps/meteor/app/livechat/server/business-hour/Single.ts @@ -49,4 +49,12 @@ export class SingleBusinessHourBehavior extends AbstractBusinessHourBehavior imp onRemoveDepartment(): Promise<void> { return Promise.resolve(); } + + onDepartmentDisabled(): Promise<void> { + return Promise.resolve(); + } + + onDepartmentArchived(): Promise<void> { + return Promise.resolve(); + } } diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 1fcf39c30a31..950e8450df4b 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -740,6 +740,8 @@ export const Livechat = { }); } + // TODO: these kind of actions should be on events instead of here + await LivechatDepartmentAgents.enableAgentsByDepartmentId(_id); return LivechatDepartmentRaw.unarchiveDepartment(_id); }, @@ -754,7 +756,11 @@ export const Livechat = { }); } - return LivechatDepartmentRaw.archiveDepartment(_id); + await LivechatDepartmentAgents.disableAgentsByDepartmentId(_id); + await LivechatDepartmentRaw.archiveDepartment(_id); + + this.logger.debug({ msg: 'Running livechat.afterDepartmentArchived callback for department:', departmentId: _id }); + await callbacks.run('livechat.afterDepartmentArchived', department); }, showConnecting() { diff --git a/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx b/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx index ea08b18ca609..50d53da351bc 100644 --- a/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx +++ b/apps/meteor/client/components/AutoCompleteDepartmentMultiple.tsx @@ -13,12 +13,14 @@ type AutoCompleteDepartmentMultipleProps = { onChange: (value: PaginatedMultiSelectOption[]) => void; onlyMyDepartments?: boolean; showArchived?: boolean; + enabled?: boolean; }; const AutoCompleteDepartmentMultiple = ({ value, onlyMyDepartments = false, showArchived = false, + enabled = false, onChange = () => undefined, }: AutoCompleteDepartmentMultipleProps) => { const t = useTranslation(); @@ -28,8 +30,8 @@ const AutoCompleteDepartmentMultiple = ({ const { itemsList: departmentsList, loadMoreItems: loadMoreDepartments } = useDepartmentsList( useMemo( - () => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments, ...(showArchived && { showArchived: true }) }), - [debouncedDepartmentsFilter, onlyMyDepartments, showArchived], + () => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments, ...(showArchived && { showArchived: true }), enabled }), + [debouncedDepartmentsFilter, enabled, onlyMyDepartments, showArchived], ), ); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts index 6575f13bbde2..b51294287d55 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts @@ -7,9 +7,12 @@ import { isEnterprise } from '../../../license/server/license'; import { businessHourLogger } from '../../../../../app/livechat/server/lib/logger'; const getAllAgentIdsWithoutDepartment = async (): Promise<string[]> => { - const agentIdsWithDepartment = ( - await LivechatDepartmentAgents.find({ departmentEnabled: true }, { projection: { agentId: 1 } }).toArray() - ).map((dept) => dept.agentId); + // Fetch departments with agents excluding archived ones (disabled ones still can be tied to business hours) + // Then find the agents that are not in any of those departments + + const departmentIds = (await LivechatDepartment.findNotArchived({ projection: { _id: 1 } }).toArray()).map(({ _id }) => _id); + + const agentIdsWithDepartment = await LivechatDepartmentAgents.findAllAgentsConnectedToListOfDepartments(departmentIds); const agentIdsWithoutDepartment = ( await Users.findUsersInRolesWithQuery( @@ -62,7 +65,10 @@ const getAgentIdsToHandle = async (businessHour: Pick<ILivechatBusinessHour, '_i ).map((dept) => dept.agentId); }; -export const openBusinessHour = async (businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>): Promise<void> => { +export const openBusinessHour = async ( + businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>, + updateLivechatStatus = true, +): Promise<void> => { const agentIds = await getAgentIdsToHandle(businessHour); businessHourLogger.debug({ msg: 'Opening business hour', @@ -72,7 +78,9 @@ export const openBusinessHour = async (businessHour: Pick<ILivechatBusinessHour, }); await Users.addBusinessHourByAgentIds(agentIds, businessHour._id); - await Users.updateLivechatStatusBasedOnBusinessHours(); + if (updateLivechatStatus) { + await Users.updateLivechatStatusBasedOnBusinessHours(); + } }; export const closeBusinessHour = async (businessHour: Pick<ILivechatBusinessHour, '_id' | 'type'>): Promise<void> => { diff --git a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts index 0fa768453e3a..3e1eb7aea652 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts @@ -1,12 +1,14 @@ import moment from 'moment'; import type { ILivechatDepartment, ILivechatBusinessHour } from '@rocket.chat/core-typings'; -import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models'; +import { LivechatDepartment, LivechatDepartmentAgents, Users } from '@rocket.chat/models'; import type { IBusinessHourBehavior } from '../../../../../app/livechat/server/business-hour/AbstractBusinessHour'; import { AbstractBusinessHourBehavior } from '../../../../../app/livechat/server/business-hour/AbstractBusinessHour'; import { filterBusinessHoursThatMustBeOpened } from '../../../../../app/livechat/server/business-hour/Helper'; import { closeBusinessHour, openBusinessHour, removeBusinessHourByAgentIds } from './Helper'; -import { businessHourLogger } from '../../../../../app/livechat/server/lib/logger'; +import { bhLogger } from '../lib/logger'; +import { settings } from '../../../../../app/settings/server'; +import { businessHourManager } from '../../../../../app/livechat/server/business-hour'; interface IBusinessHoursExtraProperties extends ILivechatBusinessHour { timezoneName: string; @@ -19,6 +21,8 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior this.onAddAgentToDepartment = this.onAddAgentToDepartment.bind(this); this.onRemoveAgentFromDepartment = this.onRemoveAgentFromDepartment.bind(this); this.onRemoveDepartment = this.onRemoveDepartment.bind(this); + this.onDepartmentArchived = this.onDepartmentArchived.bind(this); + this.onDepartmentDisabled = this.onDepartmentDisabled.bind(this); this.onNewAgentCreated = this.onNewAgentCreated.bind(this); } @@ -36,7 +40,7 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior }, }); const businessHoursToOpen = await filterBusinessHoursThatMustBeOpened(activeBusinessHours); - businessHourLogger.debug({ + bhLogger.debug({ msg: 'Starting Multiple Business Hours', totalBusinessHoursToOpen: businessHoursToOpen.length, top10BusinessHoursToOpen: businessHoursToOpen.slice(0, 10), @@ -125,13 +129,100 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior return this.handleRemoveAgentsFromDepartments(department, agentsId, options); } - async onRemoveDepartment(options: Record<string, any> = {}): Promise<any> { + async onRemoveDepartment(options: { department: ILivechatDepartment; agentsIds: string[] }): Promise<any> { + bhLogger.debug(`onRemoveDepartment: department ${options.department._id} removed`); const { department, agentsIds } = options; if (!department || !agentsIds?.length) { return options; } - const deletedDepartment = LivechatDepartment.trashFindOneById(department._id); - return this.handleRemoveAgentsFromDepartments(deletedDepartment, agentsIds, options); + return this.onDepartmentDisabled(department); + } + + async onDepartmentDisabled(department: ILivechatDepartment): Promise<void> { + if (!department.businessHourId) { + bhLogger.debug({ + msg: 'onDepartmentDisabled: department has no business hour', + departmentId: department._id, + }); + return; + } + + // Get business hour + let businessHour = await this.BusinessHourRepository.findOneById(department.businessHourId); + if (!businessHour) { + bhLogger.error({ + msg: 'onDepartmentDisabled: business hour not found', + businessHourId: department.businessHourId, + }); + return; + } + + // Unlink business hour from department + await LivechatDepartment.removeBusinessHourFromDepartmentsByIdsAndBusinessHourId([department._id], businessHour._id); + + // cleanup user's cache for default business hour and this business hour + const defaultBH = await this.BusinessHourRepository.findOneDefaultBusinessHour(); + if (!defaultBH) { + bhLogger.error('onDepartmentDisabled: default business hour not found'); + throw new Error('Default business hour not found'); + } + await this.UsersRepository.closeAgentsBusinessHoursByBusinessHourIds([businessHour._id, defaultBH._id]); + + // If i'm the only one, disable the business hour + const imTheOnlyOne = !(await LivechatDepartment.countByBusinessHourIdExcludingDepartmentId(businessHour._id, department._id)); + if (imTheOnlyOne) { + bhLogger.warn({ + msg: 'onDepartmentDisabled: department is the only one on business hour, disabling it', + departmentId: department._id, + businessHourId: businessHour._id, + }); + await this.BusinessHourRepository.disableBusinessHour(businessHour._id); + + businessHour = await this.BusinessHourRepository.findOneById(department.businessHourId); + if (!businessHour) { + bhLogger.error({ + msg: 'onDepartmentDisabled: business hour not found', + businessHourId: department.businessHourId, + }); + + throw new Error(`Business hour ${department.businessHourId} not found`); + } + } + + // start default business hour and this BH if needed + if (!settings.get('Livechat_enable_business_hours')) { + bhLogger.debug(`onDepartmentDisabled: business hours are disabled. skipping`); + return; + } + const businessHourToOpen = await filterBusinessHoursThatMustBeOpened([businessHour, defaultBH]); + for await (const bh of businessHourToOpen) { + bhLogger.debug({ + msg: 'onDepartmentDisabled: opening business hour', + businessHourId: bh._id, + }); + await openBusinessHour(bh, false); + } + + await Users.updateLivechatStatusBasedOnBusinessHours(); + + await businessHourManager.restartCronJobsIfNecessary(); + + bhLogger.debug({ + msg: 'onDepartmentDisabled: successfully processed department disabled event', + departmentId: department._id, + }); + } + + async onDepartmentArchived(department: Pick<ILivechatDepartment, '_id'>): Promise<void> { + bhLogger.debug('Processing department archived event on multiple business hours', department); + const dbDepartment = await LivechatDepartment.findOneById(department._id, { projection: { businessHourId: 1, _id: 1 } }); + + if (!dbDepartment) { + bhLogger.error(`No department found with id: ${department._id} when archiving it`); + return; + } + + return this.onDepartmentDisabled(dbDepartment); } allowAgentChangeServiceStatus(agentId: string): Promise<boolean> { @@ -147,7 +238,6 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior } // TODO: We're doing a full fledged aggregation with lookups and getting the whole array just for getting the length? :( if (!(await LivechatDepartmentAgents.findAgentsByAgentIdAndBusinessHourId(agentId, department.businessHourId)).length) { - // eslint-disable-line no-await-in-loop agentIdsToRemoveCurrentBusinessHour.push(agentId); } } diff --git a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/lib/business-hour.ts b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/lib/business-hour.ts index 216a345a7ee3..f82eb65f6624 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/lib/business-hour.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/lib/business-hour.ts @@ -1,6 +1,6 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; -import { LivechatBusinessHours } from '@rocket.chat/models'; +import { LivechatBusinessHours, LivechatDepartment } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../../../../app/authorization/server/functions/hasPermission'; import type { IPaginatedResponse, IPagination } from '../../api/lib/definition'; @@ -26,8 +26,23 @@ export async function findBusinessHours(userId: string, { offset, count, sort }: const [businessHours, total] = await Promise.all([cursor.toArray(), totalCount]); + // add departments to businessHours + const businessHoursWithDepartments = await Promise.all( + businessHours.map(async (businessHour) => { + const currentDepartments = await LivechatDepartment.findByBusinessHourId(businessHour._id, { + projection: { _id: 1 }, + }).toArray(); + + if (currentDepartments.length) { + businessHour.departments = currentDepartments; + } + + return businessHour; + }), + ); + return { - businessHours, + businessHours: businessHoursWithDepartments, count: businessHours.length, offset, total, diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts index 995ac4f8caba..653dfbda74e4 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts @@ -26,6 +26,7 @@ import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingMa import { settings } from '../../../../../app/settings/server'; import { queueLogger } from './logger'; import { getInquirySortMechanismSetting } from '../../../../../app/livechat/server/lib/settings'; +import { callbacks } from '../../../../../lib/callbacks'; export const LivechatEnterprise = { async addMonitor(username: string) { @@ -201,7 +202,7 @@ export const LivechatEnterprise = { ) { check(_id, Match.Maybe(String)); - const department = _id ? await LivechatDepartmentRaw.findOneById(_id, { projection: { _id: 1, archived: 1 } }) : null; + const department = _id ? await LivechatDepartmentRaw.findOneById(_id, { projection: { _id: 1, archived: 1, enabled: 1 } }) : null; if (!hasLicense('livechat-enterprise')) { const totalDepartments = await LivechatDepartmentRaw.countTotal(); @@ -278,6 +279,11 @@ export const LivechatEnterprise = { await updateDepartmentAgents(departmentDB._id, departmentAgents, departmentDB.enabled); } + // Disable event + if (department?.enabled && !departmentDB?.enabled) { + void callbacks.run('livechat.afterDepartmentDisabled', departmentDB); + } + return departmentDB; }, diff --git a/apps/meteor/ee/app/livechat-enterprise/server/startup.ts b/apps/meteor/ee/app/livechat-enterprise/server/startup.ts index 7ba2479dd2fb..0b63ae2c587a 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/startup.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/startup.ts @@ -39,7 +39,7 @@ Meteor.startup(async function () { } businessHourManager.registerBusinessHourBehavior(businessHours[value as keyof typeof businessHours]); if (settings.get('Livechat_enable_business_hours')) { - await businessHourManager.startManager(); + await businessHourManager.restartManager(); logger.debug(`Business hour manager started`); } }); diff --git a/apps/meteor/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js b/apps/meteor/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js index 4606e3f790d0..ba168826b435 100644 --- a/apps/meteor/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js +++ b/apps/meteor/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js @@ -30,7 +30,7 @@ const BusinessHoursMultiple = ({ values = {}, handlers = {}, className }) => { <Field className={className}> <Field.Label>{t('Departments')}</Field.Label> <Field.Row> - <AutoCompleteDepartmentMultiple value={departments} onChange={handleDepartments} /> + <AutoCompleteDepartmentMultiple value={departments} onChange={handleDepartments} enabled={true} /> </Field.Row> </Field> </> diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 69913b79e3c5..2fad995dfd1f 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -92,6 +92,8 @@ interface EventLikeCallbackSignatures { 'afterValidateLogin': (login: { user: IUser }) => void; 'afterJoinRoom': (user: IUser, room: IRoom) => void; 'beforeCreateRoom': (data: { type: IRoom['t']; extraData: { encrypted: boolean } }) => void; + 'livechat.afterDepartmentDisabled': (department: ILivechatDepartmentRecord) => void; + 'livechat.afterDepartmentArchived': (department: Pick<ILivechatDepartmentRecord, '_id'>) => void; 'afterSaveUser': ({ user, oldUser }: { user: IUser; oldUser: IUser | null }) => void; } diff --git a/apps/meteor/server/models/raw/LivechatBusinessHours.ts b/apps/meteor/server/models/raw/LivechatBusinessHours.ts index 1659740a55ef..e17cf27d6006 100644 --- a/apps/meteor/server/models/raw/LivechatBusinessHours.ts +++ b/apps/meteor/server/models/raw/LivechatBusinessHours.ts @@ -171,4 +171,8 @@ export class LivechatBusinessHoursRaw extends BaseRaw<ILivechatBusinessHour> imp } return this.col.find(query, options).toArray(); } + + disableBusinessHour(businessHourId: string): Promise<any> { + return this.updateOne({ _id: businessHourId }, { $set: { active: false } }); + } } diff --git a/apps/meteor/server/models/raw/LivechatDepartment.ts b/apps/meteor/server/models/raw/LivechatDepartment.ts index 5b1b4010dd38..2c9997b4b048 100644 --- a/apps/meteor/server/models/raw/LivechatDepartment.ts +++ b/apps/meteor/server/models/raw/LivechatDepartment.ts @@ -83,6 +83,12 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen }, sparse: true, }, + { + key: { + archived: 1, + }, + sparse: true, + }, ]; } @@ -123,6 +129,11 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen return this.find(query, options); } + countByBusinessHourIdExcludingDepartmentId(businessHourId: string, departmentId: string): Promise<number> { + const query = { businessHourId, _id: { $ne: departmentId } }; + return this.col.countDocuments(query); + } + findEnabledByBusinessHourId(businessHourId: string, options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment> { const query = { businessHourId, enabled: true }; return this.find(query, options); @@ -236,7 +247,12 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen await LivechatDepartmentAgents.setDepartmentEnabledByDepartmentId(_id, data.enabled); } - return Object.assign(record, { _id }); + const latestDept = await this.findOneById(_id); + if (!latestDept) { + throw new Error(`Department ${_id} not found`); + } + + return latestDept; } unsetFallbackDepartmentByDepartmentId(departmentId: string): Promise<Document | UpdateResult> { @@ -354,6 +370,66 @@ export class LivechatDepartmentRaw extends BaseRaw<ILivechatDepartment> implemen return this.find(query, options); } + findNotArchived(options: FindOptions<ILivechatDepartment> = {}): FindCursor<ILivechatDepartment> { + const query = { archived: { $ne: false } }; + + return this.find(query, options); + } + + getBusinessHoursWithDepartmentStatuses(): Promise< + { + _id: string; + validDepartments: string[]; + invalidDepartments: string[]; + }[] + > { + return this.col + .aggregate<{ _id: string; validDepartments: string[]; invalidDepartments: string[] }>([ + { + $match: { + businessHourId: { + $exists: true, + }, + }, + }, + { + $group: { + _id: '$businessHourId', + validDepartments: { + $push: { + $cond: { + if: { + $or: [ + { + $eq: ['$enabled', true], + }, + { + $ne: ['$archived', true], + }, + ], + }, + then: '$_id', + else: '$$REMOVE', + }, + }, + }, + invalidDepartments: { + $push: { + $cond: { + if: { + $or: [{ $eq: ['$enabled', false] }, { $eq: ['$archived', true] }], + }, + then: '$_id', + else: '$$REMOVE', + }, + }, + }, + }, + }, + ]) + .toArray(); + } + checkIfMonitorIsMonitoringDepartmentById(monitorId: string, departmentId: string): Promise<boolean> { const aggregation = [ { diff --git a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts index da709ff1136e..52f946f34835 100644 --- a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts +++ b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts @@ -357,8 +357,16 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw<ILivechatDepartmentAgen return this.col.countDocuments({ departmentId }); } + disableAgentsByDepartmentId(departmentId: string): Promise<UpdateResult | Document> { + return this.updateMany({ departmentId }, { $set: { departmentEnabled: false } }); + } + + enableAgentsByDepartmentId(departmentId: string): Promise<UpdateResult | Document> { + return this.updateMany({ departmentId }, { $set: { departmentEnabled: true } }); + } + findAllAgentsConnectedToListOfDepartments(departmentIds: string[]): Promise<string[]> { - return this.col.distinct('agentId', { departmentId: { $in: departmentIds } }); + return this.col.distinct('agentId', { departmentId: { $in: departmentIds }, departmentEnabled: true }); } } diff --git a/apps/meteor/tests/data/livechat/business-hours.ts b/apps/meteor/tests/data/livechat/business-hours.ts deleted file mode 100644 index f3335047cca3..000000000000 --- a/apps/meteor/tests/data/livechat/business-hours.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; -import { credentials, methodCall, request } from '../api-data'; - -export const saveBusinessHour = async (businessHour: ILivechatBusinessHour) => { - const { body } = await request - .post(methodCall('livechat:saveBusinessHour')) - .set(credentials) - .send({ message: JSON.stringify({ params: [businessHour], msg: 'method', method: 'livechat:saveBusinessHour', id: '101' }) }) - .expect(200); - - return JSON.parse(body.message); -}; diff --git a/apps/meteor/tests/data/livechat/businessHours.ts b/apps/meteor/tests/data/livechat/businessHours.ts index 73ccdf75d096..8ce1d91a79b9 100644 --- a/apps/meteor/tests/data/livechat/businessHours.ts +++ b/apps/meteor/tests/data/livechat/businessHours.ts @@ -1,11 +1,49 @@ import { ILivechatBusinessHour, LivechatBusinessHourTypes } from "@rocket.chat/core-typings"; import { api, credentials, methodCall, request } from "../api-data"; import { updateEESetting, updateSetting } from "../permissions.helper" -import { saveBusinessHour } from "./business-hours"; import moment from "moment"; type ISaveBhApiWorkHour = Omit<ILivechatBusinessHour, '_id' | 'ts' | 'timezone'> & { workHours: { day: string, start: string, finish: string, open: boolean }[] } & { departmentsToApplyBusinessHour?: string } & { timezoneName: string }; +// TODO: Migrate to an API call and return the business hour updated/created +export const saveBusinessHour = async (businessHour: ISaveBhApiWorkHour) => { + const { body } = await request + .post(methodCall('livechat:saveBusinessHour')) + .set(credentials) + .send({ message: JSON.stringify({ params: [businessHour], msg: 'method', method: 'livechat:saveBusinessHour', id: '101' }) }) + .expect(200); + + return JSON.parse(body.message); +}; + +export const createCustomBusinessHour = async (departments: string[]): Promise<ILivechatBusinessHour> => { + const name = `business-hour-${Date.now()}`; + const businessHour: ISaveBhApiWorkHour = { + name, + active: true, + type: LivechatBusinessHourTypes.CUSTOM, + workHours: getWorkHours(), + timezoneName: 'Asia/Calcutta', + departmentsToApplyBusinessHour: '', + }; + + if (departments.length) { + businessHour.departmentsToApplyBusinessHour = departments.join(','); + } + + await saveBusinessHour(businessHour); + + + const existingBusinessHours: ILivechatBusinessHour[] = await getAllCustomBusinessHours(); + const createdBusinessHour = existingBusinessHours.find((bh) => bh.name === name); + if (!createdBusinessHour) { + throw new Error('Could not create business hour'); + } + + return createdBusinessHour; +}; + + export const makeDefaultBusinessHourActiveAndClosed = async () => { // enable settings await updateSetting('Livechat_enable_business_hours', true); @@ -17,6 +55,7 @@ export const makeDefaultBusinessHourActiveAndClosed = async () => { .set(credentials) .send(); + // TODO: Refactor this to use openOrCloseBusinessHour() instead const workHours = businessHour.workHours as { start: string; finish: string; day: string, open: boolean }[]; const allEnabledWorkHours = workHours.map((workHour) => { workHour.open = true; @@ -53,6 +92,7 @@ export const disableDefaultBusinessHour = async () => { .set(credentials) .send(); + // TODO: Refactor this to use openOrCloseBusinessHour() instead const workHours = businessHour.workHours as { start: string; finish: string; day: string, open: boolean }[]; const allDisabledWorkHours = workHours.map((workHour) => { workHour.open = false; @@ -78,16 +118,47 @@ export const disableDefaultBusinessHour = async () => { }); } +export const removeCustomBusinessHour = async (businessHourId: string) => { + await request + .post(methodCall('livechat:removeBusinessHour')) + .set(credentials) + .send({ message: JSON.stringify({ params: [businessHourId, LivechatBusinessHourTypes.CUSTOM], msg: 'method', method: 'livechat:removeBusinessHour', id: '101' }) }) + .expect(200); +}; + +const getAllCustomBusinessHours = async (): Promise<ILivechatBusinessHour[]> => { + const response = await request.get(api('livechat/business-hours')).set(credentials).expect(200); + return (response.body.businessHours || []).filter((businessHour: ILivechatBusinessHour) => businessHour.type === LivechatBusinessHourTypes.CUSTOM); +}; + + +export const removeAllCustomBusinessHours = async () => { + const existingBusinessHours: ILivechatBusinessHour[] = await getAllCustomBusinessHours(); + + const promises = existingBusinessHours.map((businessHour) => removeCustomBusinessHour(businessHour._id)); + await Promise.all(promises); +}; + export const getDefaultBusinessHour = async (): Promise<ILivechatBusinessHour> => { const response = await request.get(api('livechat/business-hour')).set(credentials).query({ type: LivechatBusinessHourTypes.DEFAULT }).expect(200); return response.body.businessHour; }; +export const getCustomBusinessHourById = async (businessHourId: string): Promise<ILivechatBusinessHour> => { + const response = await request.get(api('livechat/business-hour')).set(credentials).query({ type: LivechatBusinessHourTypes.CUSTOM, _id: businessHourId }).expect(200); + return response.body.businessHour; +}; + export const openOrCloseBusinessHour = async (businessHour: ILivechatBusinessHour, open: boolean) => { const enabledBusinessHour = { ...businessHour, timezoneName: businessHour.timezone.name, - workHours: getWorkHours(open), + workHours: getWorkHours().map((workHour) => { + return { + ...workHour, + open, + } + }), departmentsToApplyBusinessHour: businessHour.departments?.map((department) => department._id).join(',') || '', } @@ -102,6 +173,7 @@ export const getWorkHours = (open = true): ISaveBhApiWorkHour['workHours'] => { day: moment().day(i).format('dddd'), start: '00:00', finish: '23:59', + open, }); } diff --git a/apps/meteor/tests/data/livechat/department.ts b/apps/meteor/tests/data/livechat/department.ts index 884ffaeade64..1780a83c8c07 100644 --- a/apps/meteor/tests/data/livechat/department.ts +++ b/apps/meteor/tests/data/livechat/department.ts @@ -1,33 +1,33 @@ import { faker } from '@faker-js/faker'; -import type { ILivechatDepartment, IUser } from '@rocket.chat/core-typings'; +import { expect } from 'chai'; +import type { ILivechatDepartment, IUser, LivechatDepartmentDTO } from '@rocket.chat/core-typings'; import { api, credentials, methodCall, request } from '../api-data'; import { IUserCredentialsHeader, password } from '../user'; import { createUser, login } from '../users.helper'; import { createAgent, makeAgentAvailable } from './rooms'; -import type { DummyResponse } from './utils'; -export const createDepartment = (): Promise<ILivechatDepartment> => - new Promise((resolve, reject) => { - request - .post(api('livechat/department')) - .send({ - department: { - enabled: false, - email: 'email@email.com', - showOnRegistration: true, - showOnOfflineForm: true, - name: `new department ${Date.now()}`, - description: 'created from api', - }, - }) - .set(credentials) - .end((err: Error, res: DummyResponse<ILivechatDepartment>) => { - if (err) { - return reject(err); - } - resolve(res.body.department); - }); - }); +export const NewDepartmentData = ((): Partial<ILivechatDepartment> => ({ + enabled: true, + name: `new department ${Date.now()}`, + description: 'created from api', + showOnRegistration: true, + email: faker.internet.email(), + showOnOfflineForm: true, +}))(); + +export const createDepartment = async (departmentData: Partial<ILivechatDepartment> = NewDepartmentData): Promise<ILivechatDepartment> => { + const response = await request.post(api('livechat/department')).set(credentials).send({ + department: departmentData, + }).expect(200); + return response.body.department; +}; + +export const updateDepartment = async (departmentId: string, departmentData: Partial<LivechatDepartmentDTO>): Promise<ILivechatDepartment> => { + const response = await request.put(api(`livechat/department/${ departmentId }`)).set(credentials).send({ + department: departmentData, + }).expect(200); + return response.body.department; +}; export const createDepartmentWithMethod = (initialAgents: { agentId: string, username: string }[] = []) => new Promise((resolve, reject) => { @@ -88,3 +88,23 @@ export const addOrRemoveAgentFromDepartment = async (departmentId: string, agent throw new Error('Failed to add or remove agent from department. Status code: ' + response.status + '\n' + response.body); } } + +export const archiveDepartment = async (departmentId: string): Promise<void> => { + await request.post(api(`livechat/department/${ departmentId }/archive`)).set(credentials).expect(200); +} + +export const disableDepartment = async (department: ILivechatDepartment): Promise<void> => { + department.enabled = false; + delete department._updatedAt; + const updatedDepartment = await updateDepartment(department._id, department); + expect(updatedDepartment.enabled).to.be.false; +} + +export const deleteDepartment = async (departmentId: string): Promise<void> => { + await request.delete(api(`livechat/department/${ departmentId }`)).set(credentials).expect(200); +} + +export const getDepartmentById = async (departmentId: string): Promise<ILivechatDepartment> => { + const response = await request.get(api(`livechat/department/${ departmentId }`)).set(credentials).expect(200); + return response.body.department; +}; diff --git a/apps/meteor/tests/data/livechat/rooms.ts b/apps/meteor/tests/data/livechat/rooms.ts index bceedec83b1c..92d4c9a6a045 100644 --- a/apps/meteor/tests/data/livechat/rooms.ts +++ b/apps/meteor/tests/data/livechat/rooms.ts @@ -106,22 +106,6 @@ export const createDepartment = (agents?: { agentId: string }[], name?: string): }); }; -export const deleteDepartment = (departmentId: string): Promise<unknown> => { - return new Promise((resolve, reject) => { - request - .delete(api(`livechat/department/${departmentId}`)) - .set(credentials) - .send() - .expect(200) - .end((err: Error, res: DummyResponse<ILivechatAgent>) => { - if (err) { - return reject(err); - } - resolve(res.body); - }); - }); -}; - export const createAgent = (overrideUsername?: string): Promise<ILivechatAgent> => new Promise((resolve, reject) => { request diff --git a/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts b/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts index 052193fb55fe..1423b5885682 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/10-departments.ts @@ -12,9 +12,8 @@ import { createVisitor, createLivechatRoom, getLivechatRoomInfo, - deleteDepartment, } from '../../../data/livechat/rooms'; -import { createDepartmentWithAnOnlineAgent } from '../../../data/livechat/department'; +import { createDepartmentWithAnOnlineAgent, deleteDepartment } from '../../../data/livechat/department'; import { IS_EE } from '../../../e2e/config/constants'; import { createUser } from '../../../data/users.helper'; import { createMonitor, createUnit } from '../../../data/livechat/units'; 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 d2e41fa11bec..e03306d18df0 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 @@ -1,22 +1,37 @@ /* eslint-env mocha */ -import type { ILivechatAgent, ILivechatBusinessHour } from '@rocket.chat/core-typings'; -import { ILivechatAgentStatus, LivechatBusinessHourBehaviors, LivechatBusinessHourTypes } from '@rocket.chat/core-typings'; +import type { ILivechatAgent, ILivechatBusinessHour, ILivechatDepartment } from '@rocket.chat/core-typings'; +import { LivechatBusinessHourBehaviors, LivechatBusinessHourTypes, ILivechatAgentStatus } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { getCredentials, api, request, credentials } from '../../../data/api-data'; -import { saveBusinessHour } from '../../../data/livechat/business-hours'; -import { updateEESetting, updatePermission, updateSetting } from '../../../data/permissions.helper'; +import { + getDefaultBusinessHour, + removeAllCustomBusinessHours, + saveBusinessHour, + openOrCloseBusinessHour, + createCustomBusinessHour, + getCustomBusinessHourById, + getWorkHours, +} from '../../../data/livechat/businessHours'; +import { removePermissionFromAllRoles, restorePermissionToRoles, updateSetting, updateEESetting } from '../../../data/permissions.helper'; import { IS_EE } from '../../../e2e/config/constants'; +import { + addOrRemoveAgentFromDepartment, + archiveDepartment, + createDepartmentWithAnOnlineAgent, + disableDepartment, + getDepartmentById, + deleteDepartment, +} from '../../../data/livechat/department'; +import { sleep } from '../../../../lib/utils/sleep'; import { createUser, deleteUser, getMe, login } from '../../../data/users.helper'; import { createAgent, makeAgentAvailable } from '../../../data/livechat/rooms'; -import { sleep } from '../../../../lib/utils/sleep'; -import { getDefaultBusinessHour, openOrCloseBusinessHour, getWorkHours } from '../../../data/livechat/businessHours'; import type { IUserCredentialsHeader } from '../../../data/user'; import { password } from '../../../data/user'; import { removeAgent } from '../../../data/livechat/users'; -describe('[CE] LIVECHAT - business hours', function () { +describe('LIVECHAT - business hours', function () { this.retries(0); before((done) => getCredentials(done)); @@ -28,14 +43,15 @@ describe('[CE] LIVECHAT - business hours', function () { }); let defaultBhId: any; - describe('livechat/business-hour', () => { + describe('[CE] livechat/business-hour', () => { it('should fail when user doesnt have view-livechat-business-hours permission', async () => { - await updatePermission('view-livechat-business-hours', []); + await removePermissionFromAllRoles('view-livechat-business-hours'); const response = await request.get(api('livechat/business-hour')).set(credentials).expect(403); expect(response.body.success).to.be.false; + + await restorePermissionToRoles('view-livechat-business-hours'); }); it('should fail when business hour type is not a valid BH type', async () => { - await updatePermission('view-livechat-business-hours', ['admin', 'livechat-manager']); const response = await request.get(api('livechat/business-hour')).set(credentials).query({ type: 'invalid' }).expect(200); expect(response.body.success).to.be.true; expect(response.body.businessHour).to.be.null; @@ -87,14 +103,15 @@ describe('[CE] LIVECHAT - business hours', function () { }); }); - (IS_EE ? describe : describe.skip)('[EE] LIVECHAT - business hours', () => { + (IS_EE ? describe : describe.skip)('[EE] livechat/business-hour', () => { it('should fail if user doesnt have view-livechat-business-hours permission', async () => { - await updatePermission('view-livechat-business-hours', []); + await removePermissionFromAllRoles('view-livechat-business-hours'); const response = await request.get(api('livechat/business-hours')).set(credentials).expect(403); expect(response.body.success).to.be.false; + + await restorePermissionToRoles('view-livechat-business-hours'); }); it('should return a list of business hours', async () => { - await updatePermission('view-livechat-business-hours', ['admin', 'livechat-manager']); const response = await request.get(api('livechat/business-hours')).set(credentials).expect(200); expect(response.body.success).to.be.true; expect(response.body.businessHours).to.be.an('array').with.lengthOf.greaterThan(0); @@ -233,6 +250,429 @@ describe('[CE] LIVECHAT - business hours', function () { }); }); + // Scenario: Assume we have a BH linked to a department, and we archive the department + // Expected result: + // 1) If BH is open and only linked to that department, it should be closed + // 2) If BH is open and linked to other departments, it should remain open + // 3) Agents within the archived department should be assigned to default BH + // 3.1) We'll also need to handle the case where if an agent is assigned to "dep1" + // 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', () => { + let defaultBusinessHour: ILivechatBusinessHour; + let customBusinessHour: ILivechatBusinessHour; + let deptLinkedToCustomBH: ILivechatDepartment; + let agentLinkedToDept: Awaited<ReturnType<typeof createDepartmentWithAnOnlineAgent>>['agent']; + + before(async () => { + await updateSetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.MULTIPLE); + // wait for the callbacks to be registered + await sleep(1000); + }); + + beforeEach(async () => { + // cleanup any existing business hours + await removeAllCustomBusinessHours(); + + // get default business hour + defaultBusinessHour = await getDefaultBusinessHour(); + + // close default business hour + await openOrCloseBusinessHour(defaultBusinessHour, false); + + // create custom business hour and link it to a department + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + customBusinessHour = await createCustomBusinessHour([department._id]); + agentLinkedToDept = agent; + deptLinkedToCustomBH = department; + + // open custom business hour + await openOrCloseBusinessHour(customBusinessHour, true); + }); + + it('upon archiving a department, if BH is open and only linked to that department, it should be closed', async () => { + // archive department + await archiveDepartment(deptLinkedToCustomBH._id); + + // verify if department is archived and BH link is removed + const department = await getDepartmentById(deptLinkedToCustomBH._id); + expect(department).to.be.an('object'); + expect(department).to.have.property('archived', true); + expect(department.businessHourId).to.be.undefined; + + // verify if BH is closed + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH).to.have.property('active', false); + expect(latestCustomBH.departments).to.be.an('array').that.is.empty; + }); + + it('upon archiving a department, if BH is open and linked to other departments, it should remain open', async () => { + // create another department and link it to the same BH + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + await removeAllCustomBusinessHours(); + customBusinessHour = await createCustomBusinessHour([deptLinkedToCustomBH._id, department._id]); + + // archive department + await archiveDepartment(deptLinkedToCustomBH._id); + + // verify if department is archived and BH link is removed + const archivedDepartment = await getDepartmentById(deptLinkedToCustomBH._id); + expect(archivedDepartment).to.be.an('object'); + expect(archivedDepartment).to.have.property('archived', true); + expect(archivedDepartment.businessHourId).to.be.undefined; + // verify if other department is not archived and BH link is not removed + const otherDepartment = await getDepartmentById(department._id); + expect(otherDepartment).to.be.an('object'); + expect(otherDepartment.businessHourId).to.be.equal(customBusinessHour._id); + + // verify if BH is still open + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH).to.have.property('active', true); + expect(latestCustomBH.departments).to.be.an('array').of.length(1); + expect(latestCustomBH?.departments?.[0]._id).to.be.equal(department._id); + + // cleanup + await deleteDepartment(department._id); + await deleteUser(agent.user); + }); + + it('upon archiving a department, agents within the archived department should be assigned to default BH', async () => { + await openOrCloseBusinessHour(defaultBusinessHour, true); + + // archive department + await archiveDepartment(deptLinkedToCustomBH._id); + + const latestAgent: ILivechatAgent = await getMe(agentLinkedToDept.credentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(defaultBusinessHour._id); + }); + + it('upon archiving a department, overlapping agents should still have BH within their cache', async () => { + // create another department and link it to the same BH + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + await removeAllCustomBusinessHours(); + customBusinessHour = await createCustomBusinessHour([deptLinkedToCustomBH._id, department._id]); + + // create overlapping agent by adding previous agent to newly created department + await addOrRemoveAgentFromDepartment( + department._id, + { + agentId: agentLinkedToDept.user._id, + username: agentLinkedToDept.user.username || '', + }, + true, + ); + + // archive department + await archiveDepartment(deptLinkedToCustomBH._id); + + // verify if department is archived and BH link is removed + const archivedDepartment = await getDepartmentById(deptLinkedToCustomBH._id); + expect(archivedDepartment).to.be.an('object'); + expect(archivedDepartment).to.have.property('archived', true); + expect(archivedDepartment.businessHourId).to.be.undefined; + + // verify if BH is still open + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH).to.have.property('active', true); + expect(latestCustomBH.departments).to.be.an('array').of.length(1); + expect(latestCustomBH?.departments?.[0]?._id).to.be.equal(department._id); + + // verify if overlapping agent still has BH within his cache + const latestAgent: ILivechatAgent = await getMe(agentLinkedToDept.credentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(customBusinessHour._id); + + // verify if other agent still has BH within his cache + const otherAgent: ILivechatAgent = await getMe(agent.credentials as any); + expect(otherAgent).to.be.an('object'); + expect(otherAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(otherAgent?.openBusinessHours?.[0]).to.be.equal(customBusinessHour._id); + + // cleanup + await deleteDepartment(department._id); + await deleteUser(agent.user); + }); + + afterEach(async () => { + await deleteDepartment(deptLinkedToCustomBH._id); + await deleteUser(agentLinkedToDept.user); + }); + }); + (IS_EE ? describe : describe.skip)('[EE] BH operations post department disablement', () => { + let defaultBusinessHour: ILivechatBusinessHour; + let customBusinessHour: ILivechatBusinessHour; + let deptLinkedToCustomBH: ILivechatDepartment; + let agentLinkedToDept: Awaited<ReturnType<typeof createDepartmentWithAnOnlineAgent>>['agent']; + + before(async () => { + await updateSetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.MULTIPLE); + // wait for the callbacks to be registered + await sleep(1000); + }); + + beforeEach(async () => { + // cleanup any existing business hours + await removeAllCustomBusinessHours(); + + // get default business hour + defaultBusinessHour = await getDefaultBusinessHour(); + + // close default business hour + await openOrCloseBusinessHour(defaultBusinessHour, false); + + // create custom business hour and link it to a department + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + customBusinessHour = await createCustomBusinessHour([department._id]); + agentLinkedToDept = agent; + deptLinkedToCustomBH = department; + + // open custom business hour + await openOrCloseBusinessHour(customBusinessHour, true); + }); + + it('upon disabling a department, if BH is open and only linked to that department, it should be closed', async () => { + // disable department + await disableDepartment(deptLinkedToCustomBH); + + // verify if BH link is removed + const department = await getDepartmentById(deptLinkedToCustomBH._id); + expect(department).to.be.an('object'); + expect(department.businessHourId).to.be.undefined; + + // verify if BH is closed + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH.active).to.be.false; + expect(latestCustomBH.departments).to.be.an('array').that.is.empty; + }); + + it('upon disabling a department, if BH is open and linked to other departments, it should remain open', async () => { + // create another department and link it to the same BH + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + await removeAllCustomBusinessHours(); + customBusinessHour = await createCustomBusinessHour([deptLinkedToCustomBH._id, department._id]); + + // disable department + await disableDepartment(deptLinkedToCustomBH); + + // verify if BH link is removed + const disabledDepartment = await getDepartmentById(deptLinkedToCustomBH._id); + expect(disabledDepartment).to.be.an('object'); + expect(disabledDepartment.businessHourId).to.be.undefined; + + // verify if other department BH link is not removed + const otherDepartment = await getDepartmentById(department._id); + expect(otherDepartment).to.be.an('object'); + expect(otherDepartment.businessHourId).to.be.equal(customBusinessHour._id); + + // verify if BH is still open + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH).to.have.property('active', true); + expect(latestCustomBH.departments).to.be.an('array').of.length(1); + expect(latestCustomBH?.departments?.[0]._id).to.be.equal(department._id); + + // cleanup + await deleteDepartment(department._id); + await deleteUser(agent.user); + }); + + it('upon disabling a department, agents within the disabled department should be assigned to default BH', async () => { + await openOrCloseBusinessHour(defaultBusinessHour, true); + + // disable department + await disableDepartment(deptLinkedToCustomBH); + + const latestAgent: ILivechatAgent = await getMe(agentLinkedToDept.credentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(defaultBusinessHour._id); + }); + + it('upon disabling a department, overlapping agents should still have BH within their cache', async () => { + // create another department and link it to the same BH + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + await removeAllCustomBusinessHours(); + customBusinessHour = await createCustomBusinessHour([deptLinkedToCustomBH._id, department._id]); + + // create overlapping agent by adding previous agent to newly created department + await addOrRemoveAgentFromDepartment( + department._id, + { + agentId: agentLinkedToDept.user._id, + username: agentLinkedToDept.user.username || '', + }, + true, + ); + + // disable department + await disableDepartment(deptLinkedToCustomBH); + + // verify if BH link is removed + const disabledDepartment = await getDepartmentById(deptLinkedToCustomBH._id); + expect(disabledDepartment).to.be.an('object'); + expect(disabledDepartment.businessHourId).to.be.undefined; + + // verify if BH is still open + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH).to.have.property('active', true); + expect(latestCustomBH.departments).to.be.an('array').of.length(1); + expect(latestCustomBH?.departments?.[0]?._id).to.be.equal(department._id); + + // verify if overlapping agent still has BH within his cache + const latestAgent: ILivechatAgent = await getMe(agentLinkedToDept.credentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(customBusinessHour._id); + + // verify if other agent still has BH within his cache + const otherAgent: ILivechatAgent = await getMe(agent.credentials as any); + expect(otherAgent).to.be.an('object'); + expect(otherAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(otherAgent?.openBusinessHours?.[0]).to.be.equal(customBusinessHour._id); + + // cleanup + await deleteDepartment(department._id); + await deleteUser(agent.user); + }); + + afterEach(async () => { + await deleteDepartment(deptLinkedToCustomBH._id); + await deleteUser(agentLinkedToDept.user); + }); + }); + (IS_EE ? describe : describe.skip)('[EE] BH operations post department removal', () => { + let defaultBusinessHour: ILivechatBusinessHour; + let customBusinessHour: ILivechatBusinessHour; + let deptLinkedToCustomBH: ILivechatDepartment; + let agentLinkedToDept: Awaited<ReturnType<typeof createDepartmentWithAnOnlineAgent>>['agent']; + + before(async () => { + await updateSetting('Livechat_business_hour_type', LivechatBusinessHourBehaviors.MULTIPLE); + // wait for the callbacks to be registered + await sleep(1000); + }); + + beforeEach(async () => { + // cleanup any existing business hours + await removeAllCustomBusinessHours(); + + // get default business hour + defaultBusinessHour = await getDefaultBusinessHour(); + + // close default business hour + await openOrCloseBusinessHour(defaultBusinessHour, false); + + // create custom business hour and link it to a department + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + customBusinessHour = await createCustomBusinessHour([department._id]); + agentLinkedToDept = agent; + deptLinkedToCustomBH = department; + + // open custom business hour + await openOrCloseBusinessHour(customBusinessHour, true); + }); + + it('upon deleting a department, if BH is open and only linked to that department, it should be closed', async () => { + await deleteDepartment(deptLinkedToCustomBH._id); + + // verify if BH is closed + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH.active).to.be.false; + expect(latestCustomBH.departments).to.be.an('array').that.is.empty; + }); + + it('upon deleting a department, if BH is open and linked to other departments, it should remain open', async () => { + // create another department and link it to the same BH + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + await removeAllCustomBusinessHours(); + customBusinessHour = await createCustomBusinessHour([deptLinkedToCustomBH._id, department._id]); + + await deleteDepartment(deptLinkedToCustomBH._id); + + // verify if other department BH link is not removed + const otherDepartment = await getDepartmentById(department._id); + expect(otherDepartment).to.be.an('object'); + expect(otherDepartment.businessHourId).to.be.equal(customBusinessHour._id); + + // verify if BH is still open + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH).to.have.property('active', true); + expect(latestCustomBH.departments).to.be.an('array').of.length(1); + expect(latestCustomBH?.departments?.[0]._id).to.be.equal(department._id); + + // cleanup + await deleteDepartment(department._id); + await deleteUser(agent.user); + }); + + it('upon deleting a department, agents within the disabled department should be assigned to default BH', async () => { + await openOrCloseBusinessHour(defaultBusinessHour, true); + + await deleteDepartment(deptLinkedToCustomBH._id); + + const latestAgent: ILivechatAgent = await getMe(agentLinkedToDept.credentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(defaultBusinessHour._id); + }); + + it('upon deleting a department, overlapping agents should still have BH within their cache', async () => { + // create another department and link it to the same BH + const { department, agent } = await createDepartmentWithAnOnlineAgent(); + await removeAllCustomBusinessHours(); + customBusinessHour = await createCustomBusinessHour([deptLinkedToCustomBH._id, department._id]); + + // create overlapping agent by adding previous agent to newly created department + await addOrRemoveAgentFromDepartment( + department._id, + { + agentId: agentLinkedToDept.user._id, + username: agentLinkedToDept.user.username || '', + }, + true, + ); + + await deleteDepartment(deptLinkedToCustomBH._id); + + // verify if BH is still open + const latestCustomBH = await getCustomBusinessHourById(customBusinessHour._id); + expect(latestCustomBH).to.be.an('object'); + expect(latestCustomBH).to.have.property('active', true); + expect(latestCustomBH.departments).to.be.an('array').of.length(1); + expect(latestCustomBH?.departments?.[0]?._id).to.be.equal(department._id); + + // verify if overlapping agent still has BH within his cache + const latestAgent: ILivechatAgent = await getMe(agentLinkedToDept.credentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(customBusinessHour._id); + + // verify if other agent still has BH within his cache + const otherAgent: ILivechatAgent = await getMe(agent.credentials as any); + expect(otherAgent).to.be.an('object'); + expect(otherAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(otherAgent?.openBusinessHours?.[0]).to.be.equal(customBusinessHour._id); + + // cleanup + await deleteDepartment(department._id); + await deleteUser(agent.user); + }); + + afterEach(async () => { + await deleteUser(agentLinkedToDept.user); + }); + }); describe('BH behavior upon new agent creation/deletion', () => { let defaultBH: ILivechatBusinessHour; let agent: ILivechatAgent; diff --git a/packages/model-typings/src/models/ILivechatBusinessHoursModel.ts b/packages/model-typings/src/models/ILivechatBusinessHoursModel.ts index 6ba6e003ce42..0bbb8f381b7f 100644 --- a/packages/model-typings/src/models/ILivechatBusinessHoursModel.ts +++ b/packages/model-typings/src/models/ILivechatBusinessHoursModel.ts @@ -39,4 +39,6 @@ export interface ILivechatBusinessHoursModel extends IBaseModel<ILivechatBusines type?: LivechatBusinessHourTypes, options?: any, ): Promise<ILivechatBusinessHour[]>; + + disableBusinessHour(businessHourId: string): Promise<any>; } diff --git a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts index 94c824e14f07..2d0c8c5ec69d 100644 --- a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts +++ b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts @@ -87,5 +87,7 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel<ILivechatDepa getNextBotForDepartment(departmentId: string, ignoreAgentId?: string): Promise<{ agentId: string; username: string } | undefined>; replaceUsernameOfAgentByUserId(userId: string, username: string): Promise<UpdateResult | Document>; countByDepartmentId(departmentId: string): Promise<number>; + disableAgentsByDepartmentId(departmentId: string): Promise<UpdateResult | Document>; + enableAgentsByDepartmentId(departmentId: string): Promise<UpdateResult | Document>; findAllAgentsConnectedToListOfDepartments(departmentIds: string[]): Promise<string[]>; } diff --git a/packages/model-typings/src/models/ILivechatDepartmentModel.ts b/packages/model-typings/src/models/ILivechatDepartmentModel.ts index 800800878088..271e433f39d6 100644 --- a/packages/model-typings/src/models/ILivechatDepartmentModel.ts +++ b/packages/model-typings/src/models/ILivechatDepartmentModel.ts @@ -14,6 +14,7 @@ export interface ILivechatDepartmentModel extends IBaseModel<ILivechatDepartment ): FindCursor<ILivechatDepartment>; findByBusinessHourId(businessHourId: string, options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>; + countByBusinessHourIdExcludingDepartmentId(businessHourId: string, departmentId: string): Promise<number>; findEnabledByBusinessHourId(businessHourId: string, options: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>; @@ -59,5 +60,13 @@ export interface ILivechatDepartmentModel extends IBaseModel<ILivechatDepartment findOneByIdOrName(_idOrName: string, options?: FindOptions<ILivechatDepartment>): Promise<ILivechatDepartment | null>; findByUnitIds(unitIds: string[], options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>; findActiveByUnitIds(unitIds: string[], options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>; + findNotArchived(options?: FindOptions<ILivechatDepartment>): FindCursor<ILivechatDepartment>; + getBusinessHoursWithDepartmentStatuses(): Promise< + { + _id: string; + validDepartments: string[]; + invalidDepartments: string[]; + }[] + >; checkIfMonitorIsMonitoringDepartmentById(monitorId: string, departmentId: string): Promise<boolean>; } From 7765526938c2a4b15cddfc2d1aed8d21370855e8 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Thu, 6 Jul 2023 01:05:03 -0300 Subject: [PATCH 074/149] chore: configure packages as public (#29638) --- .../notifications/client/lib/Notifications.ts | 2 +- apps/meteor/app/utils/client/lib/SDKClient.ts | 2 +- .../packages}/api-client/.eslintrc.json | 0 .../packages}/api-client/CHANGELOG.md | 0 ee/packages/api-client/LICENSE | 43 +++++++++++++++++++ .../api-client/__tests__/2fahandling.spec.ts | 0 .../packages}/api-client/jest.config.ts | 0 .../packages}/api-client/package.json | 1 - .../api-client/src/RestClientInterface.ts | 0 .../packages}/api-client/src/errors.ts | 0 .../packages}/api-client/src/index.ts | 0 .../packages}/api-client/tsconfig.json | 2 +- .../packages}/ddp-client/.eslintrc.json | 0 ee/packages/ddp-client/LICENSE | 43 +++++++++++++++++++ .../ddp-client/__examples__/simple.ts | 0 .../packages}/ddp-client/__mocks__/ws.ts | 0 .../ddp-client/__tests__/ClientStream.spec.ts | 0 .../ddp-client/__tests__/Connection.spec.ts | 0 .../__tests__/DDPDispatcher.spec.ts | 0 .../ddp-client/__tests__/DDPSDK.spec.ts | 0 .../__tests__/MinimalDDPClient.spec.ts | 0 .../ddp-client/__tests__/Timeout.spec.ts | 0 .../ddp-client/__tests__/helpers/index.ts | 0 .../wrapOnceEventIntoPromise.spec.ts | 0 .../packages}/ddp-client/jest.config.ts | 0 .../packages}/ddp-client/package.json | 4 +- .../packages}/ddp-client/src/ClientStream.ts | 0 .../packages}/ddp-client/src/Connection.ts | 0 .../packages}/ddp-client/src/DDPDispatcher.ts | 0 .../packages}/ddp-client/src/DDPSDK.ts | 0 .../ddp-client/src/MinimalDDPClient.ts | 0 .../packages}/ddp-client/src/README.md | 0 .../ddp-client/src/TimeoutControl.ts | 0 .../packages}/ddp-client/src/index.ts | 0 .../src/legacy/RocketchatSDKLegacy.ts | 5 ++- .../ddp-client/src/legacy/types/SDKLegacy.ts | 3 +- .../src/livechat/LivechatClientImpl.ts | 4 +- .../src/livechat/types/LivechatSDK.ts | 3 +- .../packages}/ddp-client/src/types/Account.ts | 0 .../ddp-client/src/types/ClientStream.ts | 0 .../ddp-client/src/types/DDPClient.ts | 0 .../ddp-client/src/types/IncomingPayload.ts | 0 .../ddp-client/src/types/OutgoingPayload.ts | 0 .../ddp-client/src/types/RemoveListener.ts | 0 .../packages}/ddp-client/src/types/SDK.ts | 0 .../ddp-client/src/types/Subscription.ts | 0 .../src/types/connectionPayloads.ts | 0 .../src/types/heartbeatsPayloads.ts | 0 .../packages/ddp-client/src/types}/methods.ts | 0 .../ddp-client/src/types/methodsPayloads.ts | 0 .../src/types/publicationPayloads.ts | 0 .../packages/ddp-client/src/types}/streams.ts | 0 .../src/wrapOnceEventIntoPromise.ts | 0 .../packages}/ddp-client/tsconfig.json | 2 +- packages/core-typings/package.json | 1 - packages/rest-typings/package.json | 1 - packages/ui-contexts/package.json | 1 + .../src/ServerContext/ServerContext.ts | 5 +-- packages/ui-contexts/src/hooks/useMethod.ts | 2 +- packages/ui-contexts/src/hooks/useStream.ts | 2 +- packages/ui-contexts/src/index.ts | 17 +++++++- yarn.lock | 10 ++--- 62 files changed, 125 insertions(+), 28 deletions(-) rename {packages => ee/packages}/api-client/.eslintrc.json (100%) rename {packages => ee/packages}/api-client/CHANGELOG.md (100%) create mode 100644 ee/packages/api-client/LICENSE rename {packages => ee/packages}/api-client/__tests__/2fahandling.spec.ts (100%) rename {packages => ee/packages}/api-client/jest.config.ts (100%) rename {packages => ee/packages}/api-client/package.json (98%) rename {packages => ee/packages}/api-client/src/RestClientInterface.ts (100%) rename {packages => ee/packages}/api-client/src/errors.ts (100%) rename {packages => ee/packages}/api-client/src/index.ts (100%) rename {packages => ee/packages}/api-client/tsconfig.json (71%) rename {packages => ee/packages}/ddp-client/.eslintrc.json (100%) create mode 100644 ee/packages/ddp-client/LICENSE rename {packages => ee/packages}/ddp-client/__examples__/simple.ts (100%) rename {packages => ee/packages}/ddp-client/__mocks__/ws.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/ClientStream.spec.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/Connection.spec.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/DDPDispatcher.spec.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/DDPSDK.spec.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/MinimalDDPClient.spec.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/Timeout.spec.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/helpers/index.ts (100%) rename {packages => ee/packages}/ddp-client/__tests__/wrapOnceEventIntoPromise.spec.ts (100%) rename {packages => ee/packages}/ddp-client/jest.config.ts (100%) rename {packages => ee/packages}/ddp-client/package.json (89%) rename {packages => ee/packages}/ddp-client/src/ClientStream.ts (100%) rename {packages => ee/packages}/ddp-client/src/Connection.ts (100%) rename {packages => ee/packages}/ddp-client/src/DDPDispatcher.ts (100%) rename {packages => ee/packages}/ddp-client/src/DDPSDK.ts (100%) rename {packages => ee/packages}/ddp-client/src/MinimalDDPClient.ts (100%) rename {packages => ee/packages}/ddp-client/src/README.md (100%) rename {packages => ee/packages}/ddp-client/src/TimeoutControl.ts (100%) rename {packages => ee/packages}/ddp-client/src/index.ts (100%) rename {packages => ee/packages}/ddp-client/src/legacy/RocketchatSDKLegacy.ts (98%) rename {packages => ee/packages}/ddp-client/src/legacy/types/SDKLegacy.ts (98%) rename {packages => ee/packages}/ddp-client/src/livechat/LivechatClientImpl.ts (98%) rename {packages => ee/packages}/ddp-client/src/livechat/types/LivechatSDK.ts (98%) rename {packages => ee/packages}/ddp-client/src/types/Account.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/ClientStream.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/DDPClient.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/IncomingPayload.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/OutgoingPayload.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/RemoveListener.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/SDK.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/Subscription.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/connectionPayloads.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/heartbeatsPayloads.ts (100%) rename {packages/ui-contexts/src/ServerContext => ee/packages/ddp-client/src/types}/methods.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/methodsPayloads.ts (100%) rename {packages => ee/packages}/ddp-client/src/types/publicationPayloads.ts (100%) rename {packages/ui-contexts/src/ServerContext => ee/packages/ddp-client/src/types}/streams.ts (100%) rename {packages => ee/packages}/ddp-client/src/wrapOnceEventIntoPromise.ts (100%) rename {packages => ee/packages}/ddp-client/tsconfig.json (81%) diff --git a/apps/meteor/app/notifications/client/lib/Notifications.ts b/apps/meteor/app/notifications/client/lib/Notifications.ts index 65c33c3da1bf..1b548c4ada10 100644 --- a/apps/meteor/app/notifications/client/lib/Notifications.ts +++ b/apps/meteor/app/notifications/client/lib/Notifications.ts @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import type { StreamKeys, StreamerCallback } from '@rocket.chat/ui-contexts/src/ServerContext/streams'; +import type { StreamKeys, StreamerCallback } from '@rocket.chat/ddp-client/src/types/streams'; import './Presence'; import { sdk } from '../../../utils/client/lib/SDKClient'; diff --git a/apps/meteor/app/utils/client/lib/SDKClient.ts b/apps/meteor/app/utils/client/lib/SDKClient.ts index 6eefccb3fdac..baf06c9a5389 100644 --- a/apps/meteor/app/utils/client/lib/SDKClient.ts +++ b/apps/meteor/app/utils/client/lib/SDKClient.ts @@ -2,7 +2,7 @@ import type { RestClientInterface } from '@rocket.chat/api-client'; import type { SDK } from '@rocket.chat/ddp-client/src/DDPSDK'; import type { ClientStream } from '@rocket.chat/ddp-client/src/types/ClientStream'; import { Emitter } from '@rocket.chat/emitter'; -import type { StreamKeys, StreamNames, StreamerCallbackArgs } from '@rocket.chat/ui-contexts/src/ServerContext/streams'; +import type { StreamKeys, StreamNames, StreamerCallbackArgs } from '@rocket.chat/ddp-client/src/types/streams'; import { DDPCommon } from 'meteor/ddp-common'; import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; diff --git a/packages/api-client/.eslintrc.json b/ee/packages/api-client/.eslintrc.json similarity index 100% rename from packages/api-client/.eslintrc.json rename to ee/packages/api-client/.eslintrc.json diff --git a/packages/api-client/CHANGELOG.md b/ee/packages/api-client/CHANGELOG.md similarity index 100% rename from packages/api-client/CHANGELOG.md rename to ee/packages/api-client/CHANGELOG.md diff --git a/ee/packages/api-client/LICENSE b/ee/packages/api-client/LICENSE new file mode 100644 index 000000000000..db5cba161765 --- /dev/null +++ b/ee/packages/api-client/LICENSE @@ -0,0 +1,43 @@ +The Rocket.Chat Enterprise Edition (EE) license (the "EE License") +Copyright (c) 2015-2023 Rocket.Chat Technologies Corp. + +With regard to the Rocket.Chat Software: + +This software and associated documentation files (the "Software") may only be +used in production, if you (and any entity that you represent) have agreed to, +and are in compliance with, the Rocket.Chat Subscription Terms of Service, +available at https://rocket.chat/terms (the "EE Terms"), or other agreement +governing the use of the Software, as agreed by you and Rocket.Chat, and otherwise +have a valid Rocket.Chat Enterprise Edition subscription for the correct number +of user seats. Subject to the foregoing sentence, you are free to modify this +Software and publish patches to the Software. You agree that Rocket.Chat and/or +its licensors (as applicable) retain all right, title and interest in and to all +such modifications and/or patches, and all such modifications and/or patches may +only be used, copied, modified, displayed, distributed, or otherwise exploited +with a valid Rocket.Chat Enterprise Edition subscription for the correct number +of user seats. Notwithstanding the foregoing, you may copy and modify the Software +for development and testing purposes, without requiring a Subscription. You agree +that Rocket.Chat and/or its licensors (as applicable) retain all right, title and +interest in and to all such modifications. You are not granted any other rights +beyond what is expressly stated herein. Subject to the foregoing, it is forbidden +to copy, merge, publish, distribute, sublicense, and/or sell the Software. + +This EE License applies only to the part of this Software that is not distributed +as part of Rocket.Chat Community Edition (CE). Any part of this Software distributed +as part of Rocket.Chat CE or is served client-side as an image, font, cascading +stylesheet (CSS), file which produces or is compiled, arranged, augmented, or combined +into client-side JavaScript, in whole or in part, is copyrighted under the MIT Expat +license. The full text of this EE License shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +For all third-party components incorporated into the Rocket.Chat Software, +those components are licensed under the original license provided by the owner +of the applicable component. diff --git a/packages/api-client/__tests__/2fahandling.spec.ts b/ee/packages/api-client/__tests__/2fahandling.spec.ts similarity index 100% rename from packages/api-client/__tests__/2fahandling.spec.ts rename to ee/packages/api-client/__tests__/2fahandling.spec.ts diff --git a/packages/api-client/jest.config.ts b/ee/packages/api-client/jest.config.ts similarity index 100% rename from packages/api-client/jest.config.ts rename to ee/packages/api-client/jest.config.ts diff --git a/packages/api-client/package.json b/ee/packages/api-client/package.json similarity index 98% rename from packages/api-client/package.json rename to ee/packages/api-client/package.json index 0d0e86d573c4..62923ece2f2c 100644 --- a/packages/api-client/package.json +++ b/ee/packages/api-client/package.json @@ -1,7 +1,6 @@ { "name": "@rocket.chat/api-client", "version": "0.0.2", - "private": true, "devDependencies": { "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", diff --git a/packages/api-client/src/RestClientInterface.ts b/ee/packages/api-client/src/RestClientInterface.ts similarity index 100% rename from packages/api-client/src/RestClientInterface.ts rename to ee/packages/api-client/src/RestClientInterface.ts diff --git a/packages/api-client/src/errors.ts b/ee/packages/api-client/src/errors.ts similarity index 100% rename from packages/api-client/src/errors.ts rename to ee/packages/api-client/src/errors.ts diff --git a/packages/api-client/src/index.ts b/ee/packages/api-client/src/index.ts similarity index 100% rename from packages/api-client/src/index.ts rename to ee/packages/api-client/src/index.ts diff --git a/packages/api-client/tsconfig.json b/ee/packages/api-client/tsconfig.json similarity index 71% rename from packages/api-client/tsconfig.json rename to ee/packages/api-client/tsconfig.json index 9d8ef0c3a373..b397e2c4421f 100644 --- a/packages/api-client/tsconfig.json +++ b/ee/packages/api-client/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.client.json", + "extends": "../../../tsconfig.base.client.json", "compilerOptions": { "module": "commonjs", "rootDir": "./src", diff --git a/packages/ddp-client/.eslintrc.json b/ee/packages/ddp-client/.eslintrc.json similarity index 100% rename from packages/ddp-client/.eslintrc.json rename to ee/packages/ddp-client/.eslintrc.json diff --git a/ee/packages/ddp-client/LICENSE b/ee/packages/ddp-client/LICENSE new file mode 100644 index 000000000000..db5cba161765 --- /dev/null +++ b/ee/packages/ddp-client/LICENSE @@ -0,0 +1,43 @@ +The Rocket.Chat Enterprise Edition (EE) license (the "EE License") +Copyright (c) 2015-2023 Rocket.Chat Technologies Corp. + +With regard to the Rocket.Chat Software: + +This software and associated documentation files (the "Software") may only be +used in production, if you (and any entity that you represent) have agreed to, +and are in compliance with, the Rocket.Chat Subscription Terms of Service, +available at https://rocket.chat/terms (the "EE Terms"), or other agreement +governing the use of the Software, as agreed by you and Rocket.Chat, and otherwise +have a valid Rocket.Chat Enterprise Edition subscription for the correct number +of user seats. Subject to the foregoing sentence, you are free to modify this +Software and publish patches to the Software. You agree that Rocket.Chat and/or +its licensors (as applicable) retain all right, title and interest in and to all +such modifications and/or patches, and all such modifications and/or patches may +only be used, copied, modified, displayed, distributed, or otherwise exploited +with a valid Rocket.Chat Enterprise Edition subscription for the correct number +of user seats. Notwithstanding the foregoing, you may copy and modify the Software +for development and testing purposes, without requiring a Subscription. You agree +that Rocket.Chat and/or its licensors (as applicable) retain all right, title and +interest in and to all such modifications. You are not granted any other rights +beyond what is expressly stated herein. Subject to the foregoing, it is forbidden +to copy, merge, publish, distribute, sublicense, and/or sell the Software. + +This EE License applies only to the part of this Software that is not distributed +as part of Rocket.Chat Community Edition (CE). Any part of this Software distributed +as part of Rocket.Chat CE or is served client-side as an image, font, cascading +stylesheet (CSS), file which produces or is compiled, arranged, augmented, or combined +into client-side JavaScript, in whole or in part, is copyrighted under the MIT Expat +license. The full text of this EE License shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +For all third-party components incorporated into the Rocket.Chat Software, +those components are licensed under the original license provided by the owner +of the applicable component. diff --git a/packages/ddp-client/__examples__/simple.ts b/ee/packages/ddp-client/__examples__/simple.ts similarity index 100% rename from packages/ddp-client/__examples__/simple.ts rename to ee/packages/ddp-client/__examples__/simple.ts diff --git a/packages/ddp-client/__mocks__/ws.ts b/ee/packages/ddp-client/__mocks__/ws.ts similarity index 100% rename from packages/ddp-client/__mocks__/ws.ts rename to ee/packages/ddp-client/__mocks__/ws.ts diff --git a/packages/ddp-client/__tests__/ClientStream.spec.ts b/ee/packages/ddp-client/__tests__/ClientStream.spec.ts similarity index 100% rename from packages/ddp-client/__tests__/ClientStream.spec.ts rename to ee/packages/ddp-client/__tests__/ClientStream.spec.ts diff --git a/packages/ddp-client/__tests__/Connection.spec.ts b/ee/packages/ddp-client/__tests__/Connection.spec.ts similarity index 100% rename from packages/ddp-client/__tests__/Connection.spec.ts rename to ee/packages/ddp-client/__tests__/Connection.spec.ts diff --git a/packages/ddp-client/__tests__/DDPDispatcher.spec.ts b/ee/packages/ddp-client/__tests__/DDPDispatcher.spec.ts similarity index 100% rename from packages/ddp-client/__tests__/DDPDispatcher.spec.ts rename to ee/packages/ddp-client/__tests__/DDPDispatcher.spec.ts diff --git a/packages/ddp-client/__tests__/DDPSDK.spec.ts b/ee/packages/ddp-client/__tests__/DDPSDK.spec.ts similarity index 100% rename from packages/ddp-client/__tests__/DDPSDK.spec.ts rename to ee/packages/ddp-client/__tests__/DDPSDK.spec.ts diff --git a/packages/ddp-client/__tests__/MinimalDDPClient.spec.ts b/ee/packages/ddp-client/__tests__/MinimalDDPClient.spec.ts similarity index 100% rename from packages/ddp-client/__tests__/MinimalDDPClient.spec.ts rename to ee/packages/ddp-client/__tests__/MinimalDDPClient.spec.ts diff --git a/packages/ddp-client/__tests__/Timeout.spec.ts b/ee/packages/ddp-client/__tests__/Timeout.spec.ts similarity index 100% rename from packages/ddp-client/__tests__/Timeout.spec.ts rename to ee/packages/ddp-client/__tests__/Timeout.spec.ts diff --git a/packages/ddp-client/__tests__/helpers/index.ts b/ee/packages/ddp-client/__tests__/helpers/index.ts similarity index 100% rename from packages/ddp-client/__tests__/helpers/index.ts rename to ee/packages/ddp-client/__tests__/helpers/index.ts diff --git a/packages/ddp-client/__tests__/wrapOnceEventIntoPromise.spec.ts b/ee/packages/ddp-client/__tests__/wrapOnceEventIntoPromise.spec.ts similarity index 100% rename from packages/ddp-client/__tests__/wrapOnceEventIntoPromise.spec.ts rename to ee/packages/ddp-client/__tests__/wrapOnceEventIntoPromise.spec.ts diff --git a/packages/ddp-client/jest.config.ts b/ee/packages/ddp-client/jest.config.ts similarity index 100% rename from packages/ddp-client/jest.config.ts rename to ee/packages/ddp-client/jest.config.ts diff --git a/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json similarity index 89% rename from packages/ddp-client/package.json rename to ee/packages/ddp-client/package.json index 83c45259e5e6..8a8c73c84436 100644 --- a/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -1,7 +1,6 @@ { "name": "@rocket.chat/ddp-client", "version": "0.0.1", - "private": true, "devDependencies": { "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", @@ -35,7 +34,6 @@ }, "dependencies": { "@rocket.chat/api-client": "workspace:^", - "@rocket.chat/rest-typings": "workspace:^", - "@rocket.chat/ui-contexts": "workspace:^" + "@rocket.chat/rest-typings": "workspace:^" } } diff --git a/packages/ddp-client/src/ClientStream.ts b/ee/packages/ddp-client/src/ClientStream.ts similarity index 100% rename from packages/ddp-client/src/ClientStream.ts rename to ee/packages/ddp-client/src/ClientStream.ts diff --git a/packages/ddp-client/src/Connection.ts b/ee/packages/ddp-client/src/Connection.ts similarity index 100% rename from packages/ddp-client/src/Connection.ts rename to ee/packages/ddp-client/src/Connection.ts diff --git a/packages/ddp-client/src/DDPDispatcher.ts b/ee/packages/ddp-client/src/DDPDispatcher.ts similarity index 100% rename from packages/ddp-client/src/DDPDispatcher.ts rename to ee/packages/ddp-client/src/DDPDispatcher.ts diff --git a/packages/ddp-client/src/DDPSDK.ts b/ee/packages/ddp-client/src/DDPSDK.ts similarity index 100% rename from packages/ddp-client/src/DDPSDK.ts rename to ee/packages/ddp-client/src/DDPSDK.ts diff --git a/packages/ddp-client/src/MinimalDDPClient.ts b/ee/packages/ddp-client/src/MinimalDDPClient.ts similarity index 100% rename from packages/ddp-client/src/MinimalDDPClient.ts rename to ee/packages/ddp-client/src/MinimalDDPClient.ts diff --git a/packages/ddp-client/src/README.md b/ee/packages/ddp-client/src/README.md similarity index 100% rename from packages/ddp-client/src/README.md rename to ee/packages/ddp-client/src/README.md diff --git a/packages/ddp-client/src/TimeoutControl.ts b/ee/packages/ddp-client/src/TimeoutControl.ts similarity index 100% rename from packages/ddp-client/src/TimeoutControl.ts rename to ee/packages/ddp-client/src/TimeoutControl.ts diff --git a/packages/ddp-client/src/index.ts b/ee/packages/ddp-client/src/index.ts similarity index 100% rename from packages/ddp-client/src/index.ts rename to ee/packages/ddp-client/src/index.ts diff --git a/packages/ddp-client/src/legacy/RocketchatSDKLegacy.ts b/ee/packages/ddp-client/src/legacy/RocketchatSDKLegacy.ts similarity index 98% rename from packages/ddp-client/src/legacy/RocketchatSDKLegacy.ts rename to ee/packages/ddp-client/src/legacy/RocketchatSDKLegacy.ts index fb307b2a83f7..aba6ffe56e31 100644 --- a/packages/ddp-client/src/legacy/RocketchatSDKLegacy.ts +++ b/ee/packages/ddp-client/src/legacy/RocketchatSDKLegacy.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-this-alias */ -import type { StreamNames, StreamKeys, StreamerCallbackArgs } from '@rocket.chat/ui-contexts/src/ServerContext/streams'; -import type { ServerMethods, ServerMethodReturn } from '@rocket.chat/ui-contexts'; + import type { IMessage, Serialized } from '@rocket.chat/core-typings'; import type { OperationParams, OperationResult } from '@rocket.chat/rest-typings'; import { Emitter } from '@rocket.chat/emitter'; @@ -21,6 +20,8 @@ import { ClientStreamImpl } from '../ClientStream'; import { AccountImpl } from '../types/Account'; import { TimeoutControl } from '../TimeoutControl'; import type { ClientStream } from '../types/ClientStream'; +import type { ServerMethodReturn, ServerMethods } from '../types/methods'; +import type { StreamNames, StreamKeys, StreamerCallbackArgs } from '../types/streams'; declare module '../ClientStream' { interface ClientStream { diff --git a/packages/ddp-client/src/legacy/types/SDKLegacy.ts b/ee/packages/ddp-client/src/legacy/types/SDKLegacy.ts similarity index 98% rename from packages/ddp-client/src/legacy/types/SDKLegacy.ts rename to ee/packages/ddp-client/src/legacy/types/SDKLegacy.ts index 5e422d3addd6..740ea96b4929 100644 --- a/packages/ddp-client/src/legacy/types/SDKLegacy.ts +++ b/ee/packages/ddp-client/src/legacy/types/SDKLegacy.ts @@ -1,7 +1,8 @@ -import type { StreamerCallbackArgs } from '@rocket.chat/ui-contexts/src/ServerContext/streams'; import type { IMessage, Serialized } from '@rocket.chat/core-typings'; import type { OperationParams, OperationResult } from '@rocket.chat/rest-typings'; +import type { StreamerCallbackArgs } from '../../types/streams'; + export interface APILegacy { users: { all(fields?: { name: 1; username: 1; status: 1; type: 1 }): Promise<Serialized<OperationResult<'GET', '/v1/users.list'>>>; diff --git a/packages/ddp-client/src/livechat/LivechatClientImpl.ts b/ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts similarity index 98% rename from packages/ddp-client/src/livechat/LivechatClientImpl.ts rename to ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts index 8005528b35c8..996e1c47eb91 100644 --- a/packages/ddp-client/src/livechat/LivechatClientImpl.ts +++ b/ee/packages/ddp-client/src/livechat/LivechatClientImpl.ts @@ -1,5 +1,3 @@ -import type { StreamNames, StreamKeys, StreamerCallbackArgs } from '@rocket.chat/ui-contexts/src/ServerContext/streams'; -import type { ServerMethods, ServerMethodReturn } from '@rocket.chat/ui-contexts'; import { Emitter } from '@rocket.chat/emitter'; import { RestClient } from '@rocket.chat/api-client'; import type { IOmnichannelRoom, Serialized } from '@rocket.chat/core-typings'; @@ -14,6 +12,8 @@ import { ConnectionImpl } from '../Connection'; import { AccountImpl } from '../types/Account'; import { TimeoutControl } from '../TimeoutControl'; import type { ClientStream } from '../types/ClientStream'; +import type { ServerMethodReturn, ServerMethods } from '../types/methods'; +import type { StreamKeys, StreamNames, StreamerCallbackArgs } from '../types/streams'; declare module '../ClientStream' { interface ClientStream { diff --git a/packages/ddp-client/src/livechat/types/LivechatSDK.ts b/ee/packages/ddp-client/src/livechat/types/LivechatSDK.ts similarity index 98% rename from packages/ddp-client/src/livechat/types/LivechatSDK.ts rename to ee/packages/ddp-client/src/livechat/types/LivechatSDK.ts index 171d6dfc2410..c0b36abadb15 100644 --- a/packages/ddp-client/src/livechat/types/LivechatSDK.ts +++ b/ee/packages/ddp-client/src/livechat/types/LivechatSDK.ts @@ -1,6 +1,7 @@ import type { Serialized } from '@rocket.chat/core-typings'; import type { OperationParams, OperationResult } from '@rocket.chat/rest-typings'; -import type { StreamerCallbackArgs } from '@rocket.chat/ui-contexts/src/ServerContext/streams'; + +import type { StreamerCallbackArgs } from '../../types/streams'; export type LivechatRoomEvents<T> = StreamerCallbackArgs<'livechat-room', `${string}`> extends [infer A] ? A extends { type: T; data: unknown } diff --git a/packages/ddp-client/src/types/Account.ts b/ee/packages/ddp-client/src/types/Account.ts similarity index 100% rename from packages/ddp-client/src/types/Account.ts rename to ee/packages/ddp-client/src/types/Account.ts diff --git a/packages/ddp-client/src/types/ClientStream.ts b/ee/packages/ddp-client/src/types/ClientStream.ts similarity index 100% rename from packages/ddp-client/src/types/ClientStream.ts rename to ee/packages/ddp-client/src/types/ClientStream.ts diff --git a/packages/ddp-client/src/types/DDPClient.ts b/ee/packages/ddp-client/src/types/DDPClient.ts similarity index 100% rename from packages/ddp-client/src/types/DDPClient.ts rename to ee/packages/ddp-client/src/types/DDPClient.ts diff --git a/packages/ddp-client/src/types/IncomingPayload.ts b/ee/packages/ddp-client/src/types/IncomingPayload.ts similarity index 100% rename from packages/ddp-client/src/types/IncomingPayload.ts rename to ee/packages/ddp-client/src/types/IncomingPayload.ts diff --git a/packages/ddp-client/src/types/OutgoingPayload.ts b/ee/packages/ddp-client/src/types/OutgoingPayload.ts similarity index 100% rename from packages/ddp-client/src/types/OutgoingPayload.ts rename to ee/packages/ddp-client/src/types/OutgoingPayload.ts diff --git a/packages/ddp-client/src/types/RemoveListener.ts b/ee/packages/ddp-client/src/types/RemoveListener.ts similarity index 100% rename from packages/ddp-client/src/types/RemoveListener.ts rename to ee/packages/ddp-client/src/types/RemoveListener.ts diff --git a/packages/ddp-client/src/types/SDK.ts b/ee/packages/ddp-client/src/types/SDK.ts similarity index 100% rename from packages/ddp-client/src/types/SDK.ts rename to ee/packages/ddp-client/src/types/SDK.ts diff --git a/packages/ddp-client/src/types/Subscription.ts b/ee/packages/ddp-client/src/types/Subscription.ts similarity index 100% rename from packages/ddp-client/src/types/Subscription.ts rename to ee/packages/ddp-client/src/types/Subscription.ts diff --git a/packages/ddp-client/src/types/connectionPayloads.ts b/ee/packages/ddp-client/src/types/connectionPayloads.ts similarity index 100% rename from packages/ddp-client/src/types/connectionPayloads.ts rename to ee/packages/ddp-client/src/types/connectionPayloads.ts diff --git a/packages/ddp-client/src/types/heartbeatsPayloads.ts b/ee/packages/ddp-client/src/types/heartbeatsPayloads.ts similarity index 100% rename from packages/ddp-client/src/types/heartbeatsPayloads.ts rename to ee/packages/ddp-client/src/types/heartbeatsPayloads.ts diff --git a/packages/ui-contexts/src/ServerContext/methods.ts b/ee/packages/ddp-client/src/types/methods.ts similarity index 100% rename from packages/ui-contexts/src/ServerContext/methods.ts rename to ee/packages/ddp-client/src/types/methods.ts diff --git a/packages/ddp-client/src/types/methodsPayloads.ts b/ee/packages/ddp-client/src/types/methodsPayloads.ts similarity index 100% rename from packages/ddp-client/src/types/methodsPayloads.ts rename to ee/packages/ddp-client/src/types/methodsPayloads.ts diff --git a/packages/ddp-client/src/types/publicationPayloads.ts b/ee/packages/ddp-client/src/types/publicationPayloads.ts similarity index 100% rename from packages/ddp-client/src/types/publicationPayloads.ts rename to ee/packages/ddp-client/src/types/publicationPayloads.ts diff --git a/packages/ui-contexts/src/ServerContext/streams.ts b/ee/packages/ddp-client/src/types/streams.ts similarity index 100% rename from packages/ui-contexts/src/ServerContext/streams.ts rename to ee/packages/ddp-client/src/types/streams.ts diff --git a/packages/ddp-client/src/wrapOnceEventIntoPromise.ts b/ee/packages/ddp-client/src/wrapOnceEventIntoPromise.ts similarity index 100% rename from packages/ddp-client/src/wrapOnceEventIntoPromise.ts rename to ee/packages/ddp-client/src/wrapOnceEventIntoPromise.ts diff --git a/packages/ddp-client/tsconfig.json b/ee/packages/ddp-client/tsconfig.json similarity index 81% rename from packages/ddp-client/tsconfig.json rename to ee/packages/ddp-client/tsconfig.json index b98ff74ba385..29b8cb051fe3 100644 --- a/packages/ddp-client/tsconfig.json +++ b/ee/packages/ddp-client/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.client.json", + "extends": "../../../tsconfig.base.client.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 1e049be63ca1..2cea7c47872c 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -1,7 +1,6 @@ { "name": "@rocket.chat/core-typings", "version": "6.2.6", - "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "eslint": "~8.43.0", diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 98cfd1bf67f6..08ecb8b1e1bd 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,7 +1,6 @@ { "name": "@rocket.chat/rest-typings", "version": "6.2.6", - "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/jest": "~29.5.2", diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 2fac15a34858..cdac72d2176b 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -22,6 +22,7 @@ }, "peerDependencies": { "@rocket.chat/core-typings": "workspace:^", + "@rocket.chat/ddp-client": "workspace:^", "@rocket.chat/emitter": "*", "@rocket.chat/fuselage-hooks": "*", "@rocket.chat/rest-typings": "workspace:^", diff --git a/packages/ui-contexts/src/ServerContext/ServerContext.ts b/packages/ui-contexts/src/ServerContext/ServerContext.ts index 7c8ad7ff486e..7e4b8e9e28fa 100644 --- a/packages/ui-contexts/src/ServerContext/ServerContext.ts +++ b/packages/ui-contexts/src/ServerContext/ServerContext.ts @@ -1,9 +1,8 @@ import type { IServerInfo, Serialized } from '@rocket.chat/core-typings'; import type { Method, OperationParams, OperationResult, PathFor, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; import { createContext } from 'react'; - -import type { StreamKeys, StreamNames, StreamerCallbackArgs } from './streams'; -import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn } from './methods'; +import type { StreamKeys, StreamNames, StreamerCallbackArgs } from '@rocket.chat/ddp-client/src/types/streams'; +import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn } from '@rocket.chat/ddp-client/src/types/methods'; export type UploadResult = { success: boolean; diff --git a/packages/ui-contexts/src/hooks/useMethod.ts b/packages/ui-contexts/src/hooks/useMethod.ts index 4381189039f8..9d59d16e6a73 100644 --- a/packages/ui-contexts/src/hooks/useMethod.ts +++ b/packages/ui-contexts/src/hooks/useMethod.ts @@ -1,7 +1,7 @@ import { useCallback, useContext } from 'react'; +import type { ServerMethodFunction, ServerMethodParameters, ServerMethods } from '@rocket.chat/ddp-client/src/types/methods'; import { ServerContext } from '../ServerContext'; -import type { ServerMethodFunction, ServerMethodParameters, ServerMethods } from '../ServerContext/methods'; export const useMethod = <MethodName extends keyof ServerMethods>(methodName: MethodName): ServerMethodFunction<MethodName> => { const { callMethod } = useContext(ServerContext); diff --git a/packages/ui-contexts/src/hooks/useStream.ts b/packages/ui-contexts/src/hooks/useStream.ts index 6aef7d9a0191..34c49c5a778c 100644 --- a/packages/ui-contexts/src/hooks/useStream.ts +++ b/packages/ui-contexts/src/hooks/useStream.ts @@ -1,4 +1,4 @@ -import type { StreamNames, StreamerEvents, StreamKeys, StreamerCallbackArgs } from '@rocket.chat/ui-contexts/src/ServerContext/streams'; +import type { StreamNames, StreamerEvents, StreamKeys, StreamerCallbackArgs } from '@rocket.chat/ddp-client/src/types/streams'; import { useContext, useMemo } from 'react'; import { ServerContext } from '../ServerContext'; diff --git a/packages/ui-contexts/src/index.ts b/packages/ui-contexts/src/index.ts index ec981344ed29..941eeeec61ec 100644 --- a/packages/ui-contexts/src/index.ts +++ b/packages/ui-contexts/src/index.ts @@ -89,8 +89,21 @@ export { useSetOutputMediaDevice } from './hooks/useSetOutputMediaDevice'; export { useSetInputMediaDevice } from './hooks/useSetInputMediaDevice'; export { useAccountsCustomFields } from './hooks/useAccountsCustomFields'; -export { ServerMethods, ServerMethodName, ServerMethodParameters, ServerMethodReturn, ServerMethodFunction } from './ServerContext/methods'; -export { StreamerEvents, StreamNames, StreamKeys, StreamerConfigs, StreamerConfig, StreamerCallbackArgs } from './ServerContext/streams'; +export { + ServerMethods, + ServerMethodName, + ServerMethodParameters, + ServerMethodReturn, + ServerMethodFunction, +} from '@rocket.chat/ddp-client/src/types/methods'; +export { + StreamerEvents, + StreamNames, + StreamKeys, + StreamerConfigs, + StreamerConfig, + StreamerCallbackArgs, +} from '@rocket.chat/ddp-client/src/types/streams'; export { UploadResult } from './ServerContext'; export { TranslationKey, TranslationLanguage } from './TranslationContext'; export { Fields } from './UserContext'; diff --git a/yarn.lock b/yarn.lock index 3ffbb18d891f..76c68d5fd8dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9213,9 +9213,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/api-client@workspace:^, @rocket.chat/api-client@workspace:packages/api-client": +"@rocket.chat/api-client@workspace:^, @rocket.chat/api-client@workspace:ee/packages/api-client": version: 0.0.0-use.local - resolution: "@rocket.chat/api-client@workspace:packages/api-client" + resolution: "@rocket.chat/api-client@workspace:ee/packages/api-client" dependencies: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" @@ -9427,13 +9427,12 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/ddp-client@workspace:^, @rocket.chat/ddp-client@workspace:packages/ddp-client": +"@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:packages/ddp-client" + resolution: "@rocket.chat/ddp-client@workspace:ee/packages/ddp-client" dependencies: "@rocket.chat/api-client": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" - "@rocket.chat/ui-contexts": "workspace:^" "@swc/core": ^1.3.66 "@swc/jest": ^0.2.26 "@types/jest": ^29.5.2 @@ -10895,6 +10894,7 @@ __metadata: use-sync-external-store: ^1.2.0 peerDependencies: "@rocket.chat/core-typings": "workspace:^" + "@rocket.chat/ddp-client": "workspace:^" "@rocket.chat/emitter": "*" "@rocket.chat/fuselage-hooks": "*" "@rocket.chat/rest-typings": "workspace:^" From 94477bd9f83ceb11bc81e8b43a260471a36c11f0 Mon Sep 17 00:00:00 2001 From: Kevin Aleman <kaleman960@gmail.com> Date: Thu, 6 Jul 2023 08:21:39 -0600 Subject: [PATCH 075/149] fix: Only update online & unavailable agents when opening business hours (#29540) --- .changeset/thirty-lobsters-vanish.md | 6 +++++ .../livechat/server/business-hour/Helper.ts | 3 +++ .../livechat/server/business-hour/Single.ts | 12 +++------ .../server/business-hour/Helper.ts | 1 + apps/meteor/server/models/raw/Users.js | 27 ++++++++++++++----- .../model-typings/src/models/IUsersModel.ts | 4 ++- 6 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 .changeset/thirty-lobsters-vanish.md diff --git a/.changeset/thirty-lobsters-vanish.md b/.changeset/thirty-lobsters-vanish.md new file mode 100644 index 000000000000..56fb91e4cbc6 --- /dev/null +++ b/.changeset/thirty-lobsters-vanish.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +Update database query to only update online & unavailable agents when opening & closing business hours diff --git a/apps/meteor/app/livechat/server/business-hour/Helper.ts b/apps/meteor/app/livechat/server/business-hour/Helper.ts index ccf0381f0c0c..2106a962e4f7 100644 --- a/apps/meteor/app/livechat/server/business-hour/Helper.ts +++ b/apps/meteor/app/livechat/server/business-hour/Helper.ts @@ -55,6 +55,9 @@ export const openBusinessHourDefault = async (): Promise<void> => { const businessHoursToOpenIds = (await filterBusinessHoursThatMustBeOpened(activeBusinessHours)).map((businessHour) => businessHour._id); businessHourLogger.debug({ msg: 'Opening default business hours', businessHoursToOpenIds }); await Users.openAgentsBusinessHoursByBusinessHourId(businessHoursToOpenIds); + if (businessHoursToOpenIds.length) { + await Users.makeAgentsWithinBusinessHourAvailable(); + } await Users.updateLivechatStatusBasedOnBusinessHours(); businessHourLogger.debug('Done opening default business hours'); }; diff --git a/apps/meteor/app/livechat/server/business-hour/Single.ts b/apps/meteor/app/livechat/server/business-hour/Single.ts index b351c480ab28..df9b3bce2a2e 100644 --- a/apps/meteor/app/livechat/server/business-hour/Single.ts +++ b/apps/meteor/app/livechat/server/business-hour/Single.ts @@ -6,13 +6,9 @@ import { openBusinessHourDefault } from './Helper'; import { businessHourLogger } from '../lib/logger'; export class SingleBusinessHourBehavior extends AbstractBusinessHourBehavior implements IBusinessHourBehavior { - async openBusinessHoursByDayAndHour(day: string, hour: string): Promise<void> { - const businessHoursIds = ( - await this.BusinessHourRepository.findActiveBusinessHoursToOpen(day, hour, LivechatBusinessHourTypes.DEFAULT, { - projection: { _id: 1 }, - }) - ).map((businessHour) => businessHour._id); - this.UsersRepository.openAgentsBusinessHoursByBusinessHourId(businessHoursIds); + async openBusinessHoursByDayAndHour(): Promise<void> { + businessHourLogger.debug('opening single business hour'); + return openBusinessHourDefault(); } async closeBusinessHoursByDayAndHour(day: string, hour: string): Promise<void> { @@ -22,7 +18,7 @@ export class SingleBusinessHourBehavior extends AbstractBusinessHourBehavior imp }) ).map((businessHour) => businessHour._id); await this.UsersRepository.closeAgentsBusinessHoursByBusinessHourIds(businessHoursIds); - this.UsersRepository.updateLivechatStatusBasedOnBusinessHours(); + await this.UsersRepository.updateLivechatStatusBasedOnBusinessHours(); } async onStartBusinessHours(): Promise<void> { diff --git a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts index b51294287d55..5a612195eb93 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts @@ -78,6 +78,7 @@ export const openBusinessHour = async ( }); await Users.addBusinessHourByAgentIds(agentIds, businessHour._id); + await Users.makeAgentsWithinBusinessHourAvailable(agentIds); if (updateLivechatStatus) { await Users.updateLivechatStatusBasedOnBusinessHours(); } diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 996c3abaebcd..d9195da6a437 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -857,9 +857,6 @@ export class UsersRaw extends BaseRaw { }; const update = { - $set: { - statusLivechat: 'available', - }, $addToSet: { openBusinessHours: { $each: businessHourIds }, }, @@ -883,6 +880,25 @@ export class UsersRaw extends BaseRaw { return this.updateMany(query, update); } + makeAgentsWithinBusinessHourAvailable(agentIds) { + const query = { + ...(agentIds && { _id: { $in: agentIds } }), + roles: 'livechat-agent', + // Exclude away users + status: 'online', + // Exclude users that are already available, maybe due to other business hour + statusLivechat: 'not-available', + }; + + const update = { + $set: { + statusLivechat: 'available', + }, + }; + + return this.updateMany(query, update); + } + removeBusinessHourByAgentIds(agentIds = [], businessHourId) { const query = { _id: { $in: agentIds }, @@ -904,9 +920,6 @@ export class UsersRaw extends BaseRaw { }; const update = { - $set: { - statusLivechat: 'available', - }, $addToSet: { openBusinessHours: businessHourId, }, @@ -947,6 +960,8 @@ export class UsersRaw extends BaseRaw { const query = { $or: [{ openBusinessHours: { $exists: false } }, { openBusinessHours: { $size: 0 } }], roles: 'livechat-agent', + // Avoid unnecessary updates + statusLivechat: 'available', ...(Array.isArray(userIds) && userIds.length > 0 && { _id: { $in: userIds } }), }; diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 70b161acb6f3..fecc7386f401 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -128,10 +128,12 @@ export interface IUsersModel extends IBaseModel<IUser> { openAgentsBusinessHoursByBusinessHourId(businessHourIds: any): any; - openAgentBusinessHoursByBusinessHourIdsAndAgentId(businessHourIds: any, agentId: any): any; + openAgentBusinessHoursByBusinessHourIdsAndAgentId(businessHourIds: string[], agentId: string): Promise<UpdateResult | Document>; addBusinessHourByAgentIds(agentIds: string[], businessHourId: string): any; + makeAgentsWithinBusinessHourAvailable(agentIds?: string[]): Promise<UpdateResult | Document>; + removeBusinessHourByAgentIds(agentIds: any, businessHourId: any): any; openBusinessHourToAgentsWithoutDepartment(agentIdsWithDepartment: any, businessHourId: any): any; From e19832ff4c20d7c7a03b68f5dcd43845eb2894d5 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Thu, 6 Jul 2023 12:04:20 -0300 Subject: [PATCH 076/149] chore: Most used emojis as `quickReactions` (#29735) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- apps/meteor/app/emoji/client/helpers.ts | 12 +++++++ .../components/message/toolbox/Toolbox.tsx | 11 +++---- .../client/contexts/EmojiPickerContext.ts | 2 ++ .../client/providers/EmojiPickerProvider.tsx | 32 +++++++++++++++++-- 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/apps/meteor/app/emoji/client/helpers.ts b/apps/meteor/app/emoji/client/helpers.ts index ac49033fe192..d9fba8720d1c 100644 --- a/apps/meteor/app/emoji/client/helpers.ts +++ b/apps/meteor/app/emoji/client/helpers.ts @@ -155,3 +155,15 @@ export const updateRecent = (recentList: string[]) => { !recentPkgList.includes(_emoji) && recentPkgList.push(_emoji); }); }; + +const getEmojiRender = (emojiName: string) => { + const emojiPackageName = emoji.list[emojiName]?.emojiPackage; + const emojiPackage = emoji.packages[emojiPackageName]; + return emojiPackage.render(emojiName); +}; + +export const getFrequentEmoji = (frequentEmoji: string[]) => { + return frequentEmoji?.map((frequentEmoji) => { + return { emoji: frequentEmoji, image: getEmojiRender(`:${frequentEmoji}:`) }; + }); +}; diff --git a/apps/meteor/client/components/message/toolbox/Toolbox.tsx b/apps/meteor/client/components/message/toolbox/Toolbox.tsx index 731502428a56..7ab5895ef6f1 100644 --- a/apps/meteor/client/components/message/toolbox/Toolbox.tsx +++ b/apps/meteor/client/components/message/toolbox/Toolbox.tsx @@ -55,7 +55,7 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): const mapSettings = useMemo(() => Object.fromEntries(settings.map((setting) => [setting._id, setting.value])), [settings]); const chat = useChat(); - const { addRecentEmoji, emojiListByCategory } = useEmojiPickerData(); + const { quickReactions, addRecentEmoji } = useEmojiPickerData(); const actionsQueryResult = useQuery(['rooms', room._id, 'messages', message._id, 'actions'] as const, async () => { const messageActions = await MessageAction.getButtons( @@ -87,14 +87,11 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): addRecentEmoji(emoji); }; - const recentList = emojiListByCategory.filter(({ key }) => key === 'recent')[0].emojis.list; - return ( <MessageToolbox> - {recentList.length > 0 && - recentList.slice(0, 3).map(({ emoji, image }) => { - return <EmojiElement small key={emoji} title={emoji} emoji={emoji} image={image} onClick={() => handleSetReaction(emoji)} />; - })} + {quickReactions.slice(0, 3).map(({ emoji, image }) => { + return <EmojiElement small key={emoji} title={emoji} emoji={emoji} image={image} onClick={() => handleSetReaction(emoji)} />; + })} {actionsQueryResult.data?.message.map((action) => ( <MessageToolboxItem onClick={(e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions })} diff --git a/apps/meteor/client/contexts/EmojiPickerContext.ts b/apps/meteor/client/contexts/EmojiPickerContext.ts index 594ae24e3e69..50490182d519 100644 --- a/apps/meteor/client/contexts/EmojiPickerContext.ts +++ b/apps/meteor/client/contexts/EmojiPickerContext.ts @@ -19,6 +19,7 @@ type EmojiPickerContextValue = { customItemsLimit: number; setCustomItemsLimit: (limit: number) => void; setActualTone: (tone: number) => void; + quickReactions: { emoji: string; image: string }[]; }; export const EmojiPickerContext = createContext<EmojiPickerContextValue | undefined>(undefined); @@ -54,4 +55,5 @@ export const useEmojiPickerData = () => ({ customItemsLimit: useEmojiPickerContext().customItemsLimit, setCustomItemsLimit: useEmojiPickerContext().setCustomItemsLimit, setActualTone: useEmojiPickerContext().setActualTone, + quickReactions: useEmojiPickerContext().quickReactions, }); diff --git a/apps/meteor/client/providers/EmojiPickerProvider.tsx b/apps/meteor/client/providers/EmojiPickerProvider.tsx index d112ccf205a7..5c9f3a8e9946 100644 --- a/apps/meteor/client/providers/EmojiPickerProvider.tsx +++ b/apps/meteor/client/providers/EmojiPickerProvider.tsx @@ -3,7 +3,7 @@ import type { ReactNode, ReactElement } from 'react'; import React, { useState, useCallback, useMemo, useEffect } from 'react'; import type { EmojiByCategory } from '../../app/emoji/client'; -import { emoji, updateRecent, createEmojiList, createPickerEmojis, CUSTOM_CATEGORY } from '../../app/emoji/client'; +import { emoji, getFrequentEmoji, updateRecent, createEmojiList, createPickerEmojis, CUSTOM_CATEGORY } from '../../app/emoji/client'; import { EmojiPickerContext } from '../contexts/EmojiPickerContext'; import EmojiPicker from '../views/composer/EmojiPicker/EmojiPicker'; @@ -18,6 +18,28 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen const [currentCategory, setCurrentCategory] = useState('recent'); const [customItemsLimit, setCustomItemsLimit] = useState(DEFAULT_ITEMS_LIMIT); + const [frequentEmojis, setFrequentEmojis] = useLocalStorage<[string, number][]>('emoji.frequent', []); + + const [quickReactions, setQuickReactions] = useState<{ emoji: string; image: string }[]>(() => + getFrequentEmoji(frequentEmojis.map(([emoji]) => emoji)), + ); + + const addFrequentEmojis = useCallback( + (emoji: string) => { + const empty: [string, number][] = frequentEmojis.some(([emojiName]) => emojiName === emoji) ? [] : [[emoji, 0]]; + + const sortedFrequent = [...empty, ...frequentEmojis] + .map(([emojiName, count]) => { + return (emojiName === emoji ? [emojiName, Math.min(count + 5, 100)] : [emojiName, Math.max(count - 1, 0)]) as [string, number]; + }) + .sort(([, frequentA], [, frequentB]) => frequentB - frequentA); + + setFrequentEmojis(sortedFrequent); + setQuickReactions(getFrequentEmoji(sortedFrequent.map(([emoji]) => emoji))); + }, + [frequentEmojis, setFrequentEmojis], + ); + // TODO: improve this update const updateEmojiListByCategory = useCallback( (categoryKey: string, limit: number = DEFAULT_ITEMS_LIMIT) => { @@ -40,6 +62,8 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen const addRecentEmoji = useCallback( (_emoji: string) => { + addFrequentEmojis(_emoji); + const recent = recentEmojis || []; const pos = recent.indexOf(_emoji as never); @@ -56,7 +80,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen emoji.packages.base.emojisByCategory.recent = recent; updateEmojiListByCategory('recent'); }, - [recentEmojis, setRecentEmojis, updateEmojiListByCategory], + [recentEmojis, setRecentEmojis, updateEmojiListByCategory, addFrequentEmojis], ); useEffect(() => { @@ -66,7 +90,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen const emojis = createPickerEmojis(customItemsLimit, actualTone, recentEmojis, setRecentEmojis); setEmojiListByCategory(emojis); - }, [actualTone, recentEmojis, customItemsLimit, currentCategory, setRecentEmojis]); + }, [actualTone, recentEmojis, customItemsLimit, currentCategory, setRecentEmojis, frequentEmojis]); const open = useCallback((ref: Element, callback: (emoji: string) => void) => { return setEmojiPicker(<EmojiPicker reference={ref} onClose={() => setEmojiPicker(null)} onPickEmoji={(emoji) => callback(emoji)} />); @@ -90,6 +114,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen customItemsLimit, setCustomItemsLimit, setActualTone, + quickReactions, }), [ emojiPicker, @@ -105,6 +130,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen setCurrentCategory, customItemsLimit, setActualTone, + quickReactions, ], ); From b62dde15f3a1799142a6cb1a93c71fd8410a3336 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Thu, 6 Jul 2023 21:12:26 +0530 Subject: [PATCH 077/149] fix: Clear MessageBox popup on message send (#29709) --- .changeset/rare-dingos-boil.md | 5 +++++ .../client/popup/hooks/useComposerBoxPopup.ts | 15 +++++++++++++++ .../body/composer/messageBox/MessageBox.tsx | 2 ++ 3 files changed, 22 insertions(+) create mode 100644 .changeset/rare-dingos-boil.md diff --git a/.changeset/rare-dingos-boil.md b/.changeset/rare-dingos-boil.md new file mode 100644 index 000000000000..563dd5b48dbe --- /dev/null +++ b/.changeset/rare-dingos-boil.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +Close message composer popup on sending message diff --git a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopup.ts b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopup.ts index 01376eb4d592..81a20f3bef12 100644 --- a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopup.ts +++ b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopup.ts @@ -28,6 +28,7 @@ type ComposerBoxPopupResult<T extends { _id: string; sort?: number }> = commandsRef: ComposerBoxPopupImperativeCommands<T>; suspended: boolean; filter: unknown; + clearPopup: () => void; } | { popup: undefined; @@ -39,6 +40,7 @@ type ComposerBoxPopupResult<T extends { _id: string; sort?: number }> = commandsRef: ComposerBoxPopupImperativeCommands<T>; suspended: boolean; filter: unknown; + clearPopup: () => void; }; const keys = { @@ -140,6 +142,7 @@ export const useComposerBoxPopup = <T extends { _id: string; sort?: number }>({ setFocused(undefined); setFilter(''); } + if (configuration) { const selector = configuration.matchSelectorRegex ?? @@ -232,6 +235,16 @@ export const useComposerBoxPopup = <T extends { _id: string; sort?: number }>({ } }); + const clearPopup = useMutableCallback(() => { + if (!popup) { + return; + } + + setPopup(undefined); + setFocused(undefined); + setFilter(''); + }); + const callbackRef = useCallback( (node: HTMLElement | null) => { if (!node) { @@ -256,6 +269,7 @@ export const useComposerBoxPopup = <T extends { _id: string; sort?: number }>({ suspended: true, commandsRef, filter: undefined, + clearPopup, }; } @@ -269,5 +283,6 @@ export const useComposerBoxPopup = <T extends { _id: string; sort?: number }>({ suspended, commandsRef, callbackRef, + clearPopup, }; }; diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx index 5819254fd842..153be6f85a5b 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx @@ -159,6 +159,7 @@ const MessageBox = ({ const handleSendMessage = useMutableCallback(() => { const text = chat.composer?.text ?? ''; chat.composer?.clear(); + clearPopup(); onSend?.({ value: text, @@ -334,6 +335,7 @@ const MessageBox = ({ commandsRef, callbackRef: c, filter, + clearPopup, } = useComposerBoxPopup<{ _id: string; sort?: number }>({ configurations: composerPopupConfig, }); From 01e39b5c4e3a5a1f9a7359a205de0f07559bb47d Mon Sep 17 00:00:00 2001 From: Heitor Tanoue <68477006+heitortanoue@users.noreply.github.com> Date: Thu, 6 Jul 2023 15:26:05 -0300 Subject: [PATCH 078/149] fix: Last message appears in extended view after deletion (#29624) Co-authored-by: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> --- .changeset/modern-geese-laugh.md | 5 +++++ .../app/lib/server/functions/deleteMessage.ts | 20 ++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 .changeset/modern-geese-laugh.md diff --git a/.changeset/modern-geese-laugh.md b/.changeset/modern-geese-laugh.md new file mode 100644 index 000000000000..acae2927ab49 --- /dev/null +++ b/.changeset/modern-geese-laugh.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Last message appears in extended view after deletion diff --git a/apps/meteor/app/lib/server/functions/deleteMessage.ts b/apps/meteor/app/lib/server/functions/deleteMessage.ts index d4e2f8c4750b..2c08cac6d024 100644 --- a/apps/meteor/app/lib/server/functions/deleteMessage.ts +++ b/apps/meteor/app/lib/server/functions/deleteMessage.ts @@ -68,26 +68,28 @@ export async function deleteMessage(message: IMessage, user: IUser): Promise<voi } } + if (showDeletedStatus) { + // TODO is there a better way to tell TS "IUser[username]" is not undefined? + await Messages.setAsDeletedByIdAndUser(message._id, user as Required<Pick<IUser, '_id' | 'username' | 'name'>>); + } else { + void api.broadcast('notify.deleteMessage', message.rid, { _id: message._id }); + } + const room = await Rooms.findOneById(message.rid, { projection: { lastMessage: 1, prid: 1, mid: 1, federated: 1 } }); - await callbacks.run('afterDeleteMessage', deletedMsg, room); // update last message if (settings.get('Store_Last_Message')) { if (!room?.lastMessage || room.lastMessage._id === message._id) { - await Rooms.resetLastMessageById(message.rid, deletedMsg); + const lastMessageNotDeleted = await Messages.getLastVisibleMessageSentWithNoTypeByRoomId(message.rid); + await Rooms.resetLastMessageById(message.rid, lastMessageNotDeleted); } } + await callbacks.run('afterDeleteMessage', deletedMsg, room); + // decrease message count await Rooms.decreaseMessageCountById(message.rid, 1); - if (showDeletedStatus) { - // TODO is there a better way to tell TS "IUser[username]" is not undefined? - await Messages.setAsDeletedByIdAndUser(message._id, user as Required<Pick<IUser, '_id' | 'username' | 'name'>>); - } else { - void api.broadcast('notify.deleteMessage', message.rid, { _id: message._id }); - } - if (bridges) { void bridges.getListenerBridge().messageEvent('IPostMessageDeleted', deletedMsg, user); } From 230bbc73a17d6edc7f3e505244c56d7ec1cb43c1 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Fri, 7 Jul 2023 01:24:23 +0530 Subject: [PATCH 079/149] chore: console warning on sending messages (#29750) --- apps/meteor/app/lib/client/methods/sendMessage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/meteor/app/lib/client/methods/sendMessage.ts b/apps/meteor/app/lib/client/methods/sendMessage.ts index 58bfbd1a0ee3..cd4a91b27333 100644 --- a/apps/meteor/app/lib/client/methods/sendMessage.ts +++ b/apps/meteor/app/lib/client/methods/sendMessage.ts @@ -36,15 +36,15 @@ Meteor.methods<ServerMethods>({ } // If the room is federated, send the message to matrix only - const federated = ChatRoom.findOne({ _id: message.rid }, { fields: { federated: 1 } })?.federated; - if (federated) { + const room = ChatRoom.findOne({ _id: message.rid }, { fields: { federated: 1, name: 1 } }); + if (room?.federated) { return; } message = await callbacks.run('beforeSaveMessage', message); await onClientMessageReceived(message as IMessage).then(function (message) { ChatMessage.insert(message); - return callbacks.run('afterSaveMessage', message); + return callbacks.run('afterSaveMessage', message, room); }); }, }); From b4d6b9fbdfc4be4a457d6be77ab2ef4dc9d07ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:16:22 -0300 Subject: [PATCH 080/149] fix: normalize `ButtonGroup` items as buttons (#29678) --- .../attachments/default/ActionAttachtment.tsx | 47 ++++++++++--------- apps/meteor/client/hooks/useExternalLink.ts | 13 +++++ .../components/RegisterWorkspaceMenu.tsx | 5 +- .../admin/settings/groups/LDAPGroupPage.tsx | 5 +- .../client/views/home/HomePageHeader.tsx | 6 ++- .../views/home/cards/CustomContentCard.tsx | 6 ++- .../views/room/components/body/LeaderBar.tsx | 5 +- 7 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 apps/meteor/client/hooks/useExternalLink.ts diff --git a/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx b/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx index 6e81c7c15296..8c72a84f6721 100644 --- a/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx +++ b/apps/meteor/client/components/message/content/attachments/default/ActionAttachtment.tsx @@ -3,29 +3,34 @@ import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage'; import type { FC } from 'react'; import React from 'react'; +import { useExternalLink } from '../../../../../hooks/useExternalLink'; import ActionAttachmentButton from './ActionAttachmentButton'; -export const ActionAttachment: FC<MessageAttachmentAction> = ({ actions }) => ( - <ButtonGroup mb='x4' small> - {actions - .filter( - ({ type, msg_in_chat_window: msgInChatWindow, url, image_url: image, text }) => - type === 'button' && (image || text) && (url || msgInChatWindow), - ) - .map(({ text, url, msgId, msg, msg_processing_type: processingType = 'sendMessage', image_url: image }, index) => { - const content = image ? <Box is='img' src={image} maxHeight={200} /> : text; - if (url) { +export const ActionAttachment: FC<MessageAttachmentAction> = ({ actions }) => { + const handleLinkClick = useExternalLink(); + + return ( + <ButtonGroup mb='x4' small> + {actions + .filter( + ({ type, msg_in_chat_window: msgInChatWindow, url, image_url: image, text }) => + type === 'button' && (image || text) && (url || msgInChatWindow), + ) + .map(({ text, url, msgId, msg, msg_processing_type: processingType = 'sendMessage', image_url: image }, index) => { + const content = image ? <Box is='img' src={image} maxHeight={200} /> : text; + if (url) { + return ( + <Button role='link' onClick={() => handleLinkClick(url)} key={index} small> + {content} + </Button> + ); + } return ( - <Button is='a' href={url} target='_blank' rel='noopener noreferrer' key={index} small> + <ActionAttachmentButton key={index} processingType={processingType} msg={msg} mid={msgId}> {content} - </Button> + </ActionAttachmentButton> ); - } - return ( - <ActionAttachmentButton key={index} processingType={processingType} msg={msg} mid={msgId}> - {content} - </ActionAttachmentButton> - ); - })} - </ButtonGroup> -); + })} + </ButtonGroup> + ); +}; diff --git a/apps/meteor/client/hooks/useExternalLink.ts b/apps/meteor/client/hooks/useExternalLink.ts new file mode 100644 index 000000000000..bdff1d847ded --- /dev/null +++ b/apps/meteor/client/hooks/useExternalLink.ts @@ -0,0 +1,13 @@ +import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; + +export const useExternalLink = () => { + const dispatchToastMessage = useToastMessageDispatch(); + + return (url: string | undefined) => { + if (!url) { + dispatchToastMessage({ message: 'Invalid url', type: 'error' }); + return; + } + window.open(url, '_blank', 'noopener noreferrer'); + }; +}; diff --git a/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx b/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx index 050ea60e210f..b24cd12bbab2 100644 --- a/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx +++ b/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx @@ -2,6 +2,7 @@ import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; +import { useExternalLink } from '../../../../hooks/useExternalLink'; import { cloudConsoleUrl } from '../constants'; import RegisteredWorkspaceModal from '../modals/RegisteredWorkspaceModal'; @@ -28,11 +29,13 @@ const RegisterWorkspaceMenu = ({ setModal(<RegisteredWorkspaceModal onClose={handleModalClose} onStatusChange={onStatusChange} />); }; + const handleLinkClick = useExternalLink(); + return ( <ButtonGroup> {isWorkspaceRegistered && isConnectedToCloud && ( <> - <Button is='a' href={cloudConsoleUrl} target='_blank' rel='noopener noreferrer'> + <Button role='link' onClick={() => handleLinkClick(cloudConsoleUrl)}> <Icon name='new-window' size='x20' pie={4} /> {t('Cloud')} </Button> diff --git a/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx b/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx index df60251ee9dd..900351877027 100644 --- a/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/LDAPGroupPage.tsx @@ -6,6 +6,7 @@ import type { FormEvent } from 'react'; import React, { memo, useMemo } from 'react'; import GenericModal from '../../../../components/GenericModal'; +import { useExternalLink } from '../../../../hooks/useExternalLink'; import { useEditableSettings } from '../../EditableSettingsContext'; import TabbedGroupPage from './TabbedGroupPage'; @@ -19,6 +20,8 @@ function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element { const setModal = useSetModal(); const closeModal = useMutableCallback(() => setModal()); + const handleLinkClick = useExternalLink(); + const editableSettings = useEditableSettings( useMemo( () => ({ @@ -130,7 +133,7 @@ function LDAPGroupPage({ _id, ...group }: ISetting): JSX.Element { <Button children={t('Test_Connection')} disabled={!ldapEnabled || changed} onClick={handleTestConnectionButtonClick} /> <Button children={t('Test_LDAP_Search')} disabled={!ldapEnabled || changed} onClick={handleSearchTestButtonClick} /> <Button children={t('LDAP_Sync_Now')} disabled={!ldapEnabled || changed} onClick={handleSyncNowButtonClick} /> - <Button is='a' href='https://go.rocket.chat/i/ldap-docs' target='_blank'> + <Button role='link' onClick={() => handleLinkClick('https://go.rocket.chat/i/ldap-docs')}> {t('LDAP_Documentation')} </Button> </> diff --git a/apps/meteor/client/views/home/HomePageHeader.tsx b/apps/meteor/client/views/home/HomePageHeader.tsx index 6be03a41e863..498e3839b032 100644 --- a/apps/meteor/client/views/home/HomePageHeader.tsx +++ b/apps/meteor/client/views/home/HomePageHeader.tsx @@ -1,5 +1,5 @@ import { Button, Icon } from '@rocket.chat/fuselage'; -import { useSetting, useTranslation, useAllPermissions } from '@rocket.chat/ui-contexts'; +import { useSetting, useTranslation, useAllPermissions, useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -11,10 +11,12 @@ const HomepageHeader = (): ReactElement => { const t = useTranslation(); const title = useSetting('Layout_Home_Title') as string; const canEditLayout = useAllPermissions(EDIT_LAYOUT_PERMISSIONS); + const settingsRoute = useRoute('admin-settings'); + return ( <PageHeader title={title} data-qa-id='home-header' role='heading'> {canEditLayout && ( - <Button is='a' href='/admin/settings/Layout' role='button'> + <Button onClick={() => settingsRoute.push({ group: 'Layout' })}> <Icon name='pencil' size='x16' /> {t('Customize')} </Button> )} diff --git a/apps/meteor/client/views/home/cards/CustomContentCard.tsx b/apps/meteor/client/views/home/cards/CustomContentCard.tsx index 43750f6ace93..8f59c00486b1 100644 --- a/apps/meteor/client/views/home/cards/CustomContentCard.tsx +++ b/apps/meteor/client/views/home/cards/CustomContentCard.tsx @@ -1,6 +1,6 @@ import { Box, Button, Icon, Tag } from '@rocket.chat/fuselage'; import { Card } from '@rocket.chat/ui-client'; -import { useRole, useSettingSetValue, useSetting, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRole, useSettingSetValue, useSetting, useToastMessageDispatch, useTranslation, useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -18,6 +18,8 @@ const CustomContentCard = (): ReactElement | null => { const isCustomContentVisible = Boolean(useSetting('Layout_Home_Custom_Block_Visible')); const isCustomContentOnly = Boolean(useSetting('Layout_Custom_Body_Only')); + const settingsRoute = useRoute('admin-settings'); + const setCustomContentVisible = useSettingSetValue('Layout_Home_Custom_Block_Visible'); const setCustomContentOnly = useSettingSetValue('Layout_Custom_Body_Only'); @@ -63,7 +65,7 @@ const CustomContentCard = (): ReactElement | null => { </Box> <Card.FooterWrapper> <Card.Footer> - <Button role='link' is='a' href='/admin/settings/Layout' title={t('Layout_Home_Page_Content')}> + <Button onClick={() => settingsRoute.push({ group: 'Layout' })} title={t('Layout_Home_Page_Content')}> {t('Customize_Content')} </Button> <Button diff --git a/apps/meteor/client/views/room/components/body/LeaderBar.tsx b/apps/meteor/client/views/room/components/body/LeaderBar.tsx index ce1fc8e631cc..afc28bc63220 100644 --- a/apps/meteor/client/views/room/components/body/LeaderBar.tsx +++ b/apps/meteor/client/views/room/components/body/LeaderBar.tsx @@ -8,6 +8,7 @@ import React, { memo, useCallback, useMemo } from 'react'; import { isTruthy } from '../../../../../lib/isTruthy'; import { ReactiveUserStatus } from '../../../../components/UserStatus'; import UserAvatar from '../../../../components/avatar/UserAvatar'; +import { useExternalLink } from '../../../../hooks/useExternalLink'; import { roomCoordinator } from '../../../../lib/rooms/roomCoordinator'; type LeaderBarProps = { @@ -21,6 +22,8 @@ type LeaderBarProps = { const LeaderBar = ({ _id, name, username, visible, onAvatarClick }: LeaderBarProps): ReactElement => { const t = useTranslation(); + const handleLinkClick = useExternalLink(); + const chatNowLink = useMemo(() => roomCoordinator.getRouteLink('d', { name: username }) || undefined, [username]); const handleAvatarClick = useCallback( @@ -74,7 +77,7 @@ const LeaderBar = ({ _id, name, username, visible, onAvatarClick }: LeaderBarPro </Box> </Box> </Box> - <Button is='a' href={chatNowLink}> + <Button role='link' onClick={() => handleLinkClick(chatNowLink)}> {t('Chat_Now')} </Button> </Box> From 0113c62a5cdd53b2effc0d89061355d1378b5dab Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Thu, 6 Jul 2023 18:46:31 -0300 Subject: [PATCH 081/149] regression: Omit `quickReactions` based on `MessageContext` (#29753) --- .../client/components/message/toolbox/Toolbox.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/meteor/client/components/message/toolbox/Toolbox.tsx b/apps/meteor/client/components/message/toolbox/Toolbox.tsx index 7ab5895ef6f1..4f14daf9ac67 100644 --- a/apps/meteor/client/components/message/toolbox/Toolbox.tsx +++ b/apps/meteor/client/components/message/toolbox/Toolbox.tsx @@ -46,9 +46,9 @@ type ToolboxProps = { const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): ReactElement | null => { const t = useTranslation(); + const user = useUser(); const settings = useSettings(); - const user = useUser(); const context = getMessageContext(message, room, messageContext); @@ -82,6 +82,8 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): return null; } + const isReactionAllowed = actionsQueryResult.data?.message.find(({ id }) => id === 'reaction-message'); + const handleSetReaction = (emoji: string) => { sdk.call('setReaction', `:${emoji}:`, message._id); addRecentEmoji(emoji); @@ -89,9 +91,10 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): return ( <MessageToolbox> - {quickReactions.slice(0, 3).map(({ emoji, image }) => { - return <EmojiElement small key={emoji} title={emoji} emoji={emoji} image={image} onClick={() => handleSetReaction(emoji)} />; - })} + {isReactionAllowed && + quickReactions.slice(0, 3).map(({ emoji, image }) => { + return <EmojiElement small key={emoji} title={emoji} emoji={emoji} image={image} onClick={() => handleSetReaction(emoji)} />; + })} {actionsQueryResult.data?.message.map((action) => ( <MessageToolboxItem onClick={(e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions })} From e846d873b77f6f2885fa66c621b118a1644c4cf3 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Thu, 6 Jul 2023 18:57:16 -0300 Subject: [PATCH 082/149] feat: Introduce Feature Preview page (#29698) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- .changeset/fair-rivers-occur.md | 6 + apps/meteor/app/reactions/client/init.js | 2 +- .../client/lib/messageActionDefault.ts | 2 +- .../components/message/toolbox/Toolbox.tsx | 7 +- apps/meteor/client/hooks/useFeaturePreview.ts | 21 +++ .../client/hooks/useFeaturePreviewList.ts | 44 ++++++ .../sidebar/header/hooks/useAccountItems.tsx | 25 ++++ apps/meteor/client/sidebar/header/index.tsx | 9 +- .../AccountFeaturePreviewBadge.tsx | 22 +++ .../AccountFeaturePreviewPage.tsx | 136 ++++++++++++++++++ apps/meteor/client/views/account/routes.tsx | 9 ++ .../{sidebarItems.ts => sidebarItems.tsx} | 11 ++ .../rocketchat-i18n/i18n/en.i18n.json | 8 ++ .../images/featurePreview/quick-reactions.png | Bin 0 -> 5613 bytes apps/meteor/server/settings/accounts.ts | 4 + .../v1/users/UsersSetPreferenceParamsPOST.ts | 12 ++ yarn.lock | 40 +++--- 17 files changed, 327 insertions(+), 31 deletions(-) create mode 100644 .changeset/fair-rivers-occur.md create mode 100644 apps/meteor/client/hooks/useFeaturePreview.ts create mode 100644 apps/meteor/client/hooks/useFeaturePreviewList.ts create mode 100644 apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx create mode 100644 apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx rename apps/meteor/client/views/account/{sidebarItems.ts => sidebarItems.tsx} (76%) create mode 100644 apps/meteor/public/images/featurePreview/quick-reactions.png diff --git a/.changeset/fair-rivers-occur.md b/.changeset/fair-rivers-occur.md new file mode 100644 index 000000000000..2d7a78a23155 --- /dev/null +++ b/.changeset/fair-rivers-occur.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +feat: Introduce Feature Preview page diff --git a/apps/meteor/app/reactions/client/init.js b/apps/meteor/app/reactions/client/init.js index 4c77531050b2..05ac899de1d9 100644 --- a/apps/meteor/app/reactions/client/init.js +++ b/apps/meteor/app/reactions/client/init.js @@ -39,7 +39,7 @@ Meteor.startup(function () { return true; }, - order: -2, + order: -3, group: ['message', 'menu'], }); }); diff --git a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts index 6d3f856e2b9c..d00e6bf9675d 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageActionDefault.ts @@ -110,7 +110,7 @@ Meteor.startup(async function () { return true; }, - order: -3, + order: -2, group: ['message', 'menu'], }); diff --git a/apps/meteor/client/components/message/toolbox/Toolbox.tsx b/apps/meteor/client/components/message/toolbox/Toolbox.tsx index 4f14daf9ac67..2c9a3ccae95b 100644 --- a/apps/meteor/client/components/message/toolbox/Toolbox.tsx +++ b/apps/meteor/client/components/message/toolbox/Toolbox.tsx @@ -10,6 +10,7 @@ import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/M import { MessageAction } from '../../../../app/ui-utils/client/lib/MessageAction'; import { sdk } from '../../../../app/utils/client/lib/SDKClient'; import { useEmojiPickerData } from '../../../contexts/EmojiPickerContext'; +import { useFeaturePreview } from '../../../hooks/useFeaturePreview'; import EmojiElement from '../../../views/composer/EmojiPicker/EmojiElement'; import { useIsSelecting } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoTranslate'; @@ -47,9 +48,10 @@ type ToolboxProps = { const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): ReactElement | null => { const t = useTranslation(); const user = useUser(); - const settings = useSettings(); + const quickReactionsEnabled = useFeaturePreview('quickReactions'); + const context = getMessageContext(message, room, messageContext); const mapSettings = useMemo(() => Object.fromEntries(settings.map((setting) => [setting._id, setting.value])), [settings]); @@ -91,7 +93,8 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): return ( <MessageToolbox> - {isReactionAllowed && + {quickReactionsEnabled && + isReactionAllowed && quickReactions.slice(0, 3).map(({ emoji, image }) => { return <EmojiElement small key={emoji} title={emoji} emoji={emoji} image={image} onClick={() => handleSetReaction(emoji)} />; })} diff --git a/apps/meteor/client/hooks/useFeaturePreview.ts b/apps/meteor/client/hooks/useFeaturePreview.ts new file mode 100644 index 000000000000..fcfc4ef85c82 --- /dev/null +++ b/apps/meteor/client/hooks/useFeaturePreview.ts @@ -0,0 +1,21 @@ +import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; + +import type { FeaturesAvailable, FeaturePreviewProps } from './useFeaturePreviewList'; + +export const useFeaturePreview = (featureName: FeaturesAvailable) => { + const featurePreviewEnabled = useSetting('Accounts_AllowFeaturePreview'); + const features = useUserPreference<FeaturePreviewProps[]>('featuresPreview'); + + const currentFeature = features?.find((feature) => feature.name === featureName); + + if (!featurePreviewEnabled) { + return false; + } + + if (!currentFeature) { + console.error(`Feature ${featureName} not found`); + return false; + } + + return currentFeature.value; +}; diff --git a/apps/meteor/client/hooks/useFeaturePreviewList.ts b/apps/meteor/client/hooks/useFeaturePreviewList.ts new file mode 100644 index 000000000000..b4e9a1dee6d3 --- /dev/null +++ b/apps/meteor/client/hooks/useFeaturePreviewList.ts @@ -0,0 +1,44 @@ +import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; + +export type FeaturesAvailable = 'quickReactions'; + +export type FeaturePreviewProps = { + name: FeaturesAvailable; + i18n: TranslationKey; + description: TranslationKey; + group: 'Message' | 'Navigation'; + imageUrl?: string; + value: boolean; +}; + +export const defaultFeaturesPreview: FeaturePreviewProps[] = [ + { + name: 'quickReactions', + i18n: 'Quick_reactions', + description: 'Quick_reactions_description', + group: 'Message', + imageUrl: 'images/featurePreview/quick-reactions.png', + value: false, + }, +]; + +export const useFeaturePreviewList = () => { + const featurePreviewEnabled = useSetting<boolean>('Accounts_AllowFeaturePreview'); + const userFeaturesPreview = useUserPreference<FeaturePreviewProps[]>('featuresPreview'); + + if (!featurePreviewEnabled) { + return { unseenFeatures: 0, features: [] as FeaturePreviewProps[], featurePreviewEnabled }; + } + + const unseenFeatures = defaultFeaturesPreview.filter( + (feature) => !userFeaturesPreview?.find((userFeature) => userFeature.name === feature.name), + ).length; + + const mergedFeatures = defaultFeaturesPreview.map((feature) => { + const userFeature = userFeaturesPreview?.find((userFeature) => userFeature.name === feature.name); + return { ...feature, ...userFeature }; + }); + + return { unseenFeatures, features: mergedFeatures, featurePreviewEnabled }; +}; diff --git a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx index b46923a562e8..6265a380fe67 100644 --- a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx @@ -1,21 +1,45 @@ +import { Badge } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useLogout, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; +import { useFeaturePreviewList, defaultFeaturesPreview } from '../../../hooks/useFeaturePreviewList'; export const useAccountItems = (): GenericMenuItemProps[] => { const t = useTranslation(); const accountRoute = useRoute('account-index'); + const featurePreviewRoute = useRoute('feature-preview'); + const { unseenFeatures, featurePreviewEnabled } = useFeaturePreviewList(); + const logout = useLogout(); const handleMyAccount = useMutableCallback(() => { accountRoute.push({}); }); + const handleFeaturePreview = useMutableCallback(() => { + featurePreviewRoute.push(); + }); + const handleLogout = useMutableCallback(() => { logout(); }); + const featurePreviewItem = { + id: 'feature-preview', + icon: 'flask' as const, + content: t('Feature_preview'), + onClick: handleFeaturePreview, + ...(unseenFeatures > 0 && { + addon: () => ( + <Badge variant='primary' aria-label={t('Unseen_features')}> + {unseenFeatures} + </Badge> + ), + }), + }; + return [ { id: 'my-account', @@ -23,6 +47,7 @@ export const useAccountItems = (): GenericMenuItemProps[] => { content: t('My_Account'), onClick: handleMyAccount, }, + ...(featurePreviewEnabled && defaultFeaturesPreview.length > 0 ? [featurePreviewItem] : []), { id: 'logout', icon: 'sign-out', diff --git a/apps/meteor/client/sidebar/header/index.tsx b/apps/meteor/client/sidebar/header/index.tsx index 1027474aaec2..95b225c0a242 100644 --- a/apps/meteor/client/sidebar/header/index.tsx +++ b/apps/meteor/client/sidebar/header/index.tsx @@ -13,18 +13,13 @@ import Login from './actions/Login'; import Search from './actions/Search'; import Sort from './actions/Sort'; -// TODO: Remove styles from here const HeaderWithData = (): ReactElement => { - const user = useUser(); const t = useTranslation(); + const user = useUser(); return ( <> - <Sidebar.TopBar.Section - {...{ - style: { flexShrink: 0 }, - }} - > + <Sidebar.TopBar.Section> {user ? <UserMenu user={user} /> : <UserAvatarWithStatus />} <Sidebar.TopBar.Actions> <Home title={t('Home')} /> diff --git a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx new file mode 100644 index 000000000000..631a2b76c625 --- /dev/null +++ b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx @@ -0,0 +1,22 @@ +import { Badge } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; + +import { useFeaturePreviewList } from '../../../hooks/useFeaturePreviewList'; + +const AccountFeaturePreviewBadge = () => { + const t = useTranslation(); + const { unseenFeatures } = useFeaturePreviewList(); + + if (!unseenFeatures) { + return null; + } + + return ( + <Badge variant='primary' aria-label={t('Unseen_features')}> + {unseenFeatures} + </Badge> + ); +}; + +export default AccountFeaturePreviewBadge; diff --git a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx new file mode 100644 index 000000000000..414aa2377490 --- /dev/null +++ b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx @@ -0,0 +1,136 @@ +import { css } from '@rocket.chat/css-in-js'; +import { + ButtonGroup, + Button, + Box, + Field, + ToggleSwitch, + FieldGroup, + States, + StatesIcon, + StatesTitle, + Accordion, +} from '@rocket.chat/fuselage'; +import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import { useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; +import type { ChangeEvent } from 'react'; +import React, { useEffect, Fragment } from 'react'; +import { useForm } from 'react-hook-form'; + +import Page from '../../../components/Page'; +import type { FeaturePreviewProps } from '../../../hooks/useFeaturePreviewList'; +import { useFeaturePreviewList } from '../../../hooks/useFeaturePreviewList'; + +const AccountFeaturePreviewPage = () => { + const t = useTranslation(); + const dispatchToastMessage = useToastMessageDispatch(); + const { features, unseenFeatures } = useFeaturePreviewList(); + + const setUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); + + useEffect(() => { + if (unseenFeatures) { + const featuresPreview = features.map((feature) => ({ + name: feature.name, + value: feature.value, + })); + + void setUserPreferences({ data: { featuresPreview } }); + } + }, [setUserPreferences, features, unseenFeatures]); + + const { + watch, + formState: { isDirty }, + setValue, + handleSubmit, + reset, + } = useForm({ + defaultValues: { featuresPreview: features }, + }); + + const { featuresPreview } = watch(); + + const handleSave = async () => { + try { + await setUserPreferences({ data: { featuresPreview } }); + dispatchToastMessage({ type: 'success', message: t('Preferences_saved') }); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } finally { + reset({ featuresPreview }); + } + }; + + const handleFeatures = (e: ChangeEvent<HTMLInputElement>) => { + const updated = featuresPreview.map((item) => (item.name === e.target.name ? { ...item, value: e.target.checked } : item)); + setValue('featuresPreview', updated, { shouldDirty: true }); + }; + + const grouppedFeaturesPreview = Object.entries( + featuresPreview.reduce((result, currentValue) => { + (result[currentValue.group] = result[currentValue.group] || []).push(currentValue); + return result; + }, {} as Record<FeaturePreviewProps['group'], FeaturePreviewProps[]>), + ); + + return ( + <Page> + <Page.Header title={t('Feature_preview')}> + <ButtonGroup> + <Button primary disabled={!isDirty} onClick={handleSubmit(handleSave)}> + {t('Save_changes')} + </Button> + </ButtonGroup> + </Page.Header> + <Page.ScrollableContentWithShadow> + <Box maxWidth='x600' w='full' alignSelf='center'> + {featuresPreview.length === 0 && ( + <States> + <StatesIcon name='magnifier' /> + <StatesTitle>{t('No_feature_to_preview')}</StatesTitle> + </States> + )} + {featuresPreview.length > 0 && ( + <> + <Box + className={css` + white-space: break-spaces; + `} + pbe='x24' + fontScale='p1' + > + {t('Feature_preview_page_description')} + </Box> + <Accordion> + {grouppedFeaturesPreview?.map(([group, features], index) => ( + <Accordion.Item defaultExpanded={index === 0} key={group} title={t(group as TranslationKey)}> + <FieldGroup> + {features.map((feature) => ( + <Fragment key={feature.name}> + <Field> + <Box display='flex' flexDirection='row' justifyContent='spaceBetween' flexGrow={1}> + <Field.Label>{t(feature.i18n)}</Field.Label> + <Field.Row> + <Box mie='x12'>{t('Enabled')}</Box> + <ToggleSwitch checked={feature.value} name={feature.name} onChange={handleFeatures} /> + </Field.Row> + </Box> + {feature.description && <Field.Hint mbs='x12'>{t(feature.description)}</Field.Hint>} + </Field> + {feature.imageUrl && <Box is='img' width='100%' height='auto' mbs='x16' src={feature.imageUrl} />} + </Fragment> + ))} + </FieldGroup> + </Accordion.Item> + ))} + </Accordion> + </> + )} + </Box> + </Page.ScrollableContentWithShadow> + </Page> + ); +}; + +export default AccountFeaturePreviewPage; diff --git a/apps/meteor/client/views/account/routes.tsx b/apps/meteor/client/views/account/routes.tsx index d31287fa7a57..f6a49eb268de 100644 --- a/apps/meteor/client/views/account/routes.tsx +++ b/apps/meteor/client/views/account/routes.tsx @@ -32,6 +32,10 @@ declare module '@rocket.chat/ui-contexts' { pathname: '/account/omnichannel'; pattern: '/account/omnichannel'; }; + 'feature-preview': { + pathname: '/account/feature-preview'; + pattern: '/account/feature-preview'; + }; } } @@ -70,3 +74,8 @@ registerAccountRoute('/omnichannel', { name: 'omnichannel', component: lazy(() => import('./omnichannel/OmnichannelPreferencesPage')), }); + +registerAccountRoute('/feature-preview', { + name: 'feature-preview', + component: lazy(() => import('./featurePreview/AccountFeaturePreviewPage')), +}); diff --git a/apps/meteor/client/views/account/sidebarItems.ts b/apps/meteor/client/views/account/sidebarItems.tsx similarity index 76% rename from apps/meteor/client/views/account/sidebarItems.ts rename to apps/meteor/client/views/account/sidebarItems.tsx index c2810bf48564..4087774d98cc 100644 --- a/apps/meteor/client/views/account/sidebarItems.ts +++ b/apps/meteor/client/views/account/sidebarItems.tsx @@ -1,6 +1,10 @@ +import React from 'react'; + import { hasPermission, hasAtLeastOnePermission } from '../../../app/authorization/client'; import { settings } from '../../../app/settings/client'; +import { defaultFeaturesPreview } from '../../hooks/useFeaturePreviewList'; import { createSidebarItems } from '../../lib/createSidebarItems'; +import AccountFeaturePreviewBadge from './featurePreview/AccountFeaturePreviewBadge'; export const { registerSidebarItem: registerAccountSidebarItem, @@ -43,4 +47,11 @@ export const { icon: 'headset', permissionGranted: (): boolean => hasAtLeastOnePermission(['send-omnichannel-chat-transcript', 'request-pdf-transcript']), }, + { + href: '/account/feature-preview', + i18nLabel: 'Feature_preview', + icon: 'flask', + badge: () => <AccountFeaturePreviewBadge />, + permissionGranted: () => settings.get('Accounts_AllowFeaturePreview') && defaultFeaturesPreview?.length > 0, + }, ]); diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index ec6c27d3799b..6fe395013797 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -65,6 +65,7 @@ "Accounts_AllowInvisibleStatusOption": "Allow Invisible status option", "Accounts_AllowEmailChange": "Allow Email Change", "Accounts_AllowEmailNotifications": "Allow Email Notifications", + "Accounts_AllowFeaturePreview": "Allow Feature Preview", "Accounts_AllowPasswordChange": "Allow Password Change", "Accounts_AllowPasswordChangeForOAuthUsers": "Allow Password Change for OAuth Users", "Accounts_AllowRealNameChange": "Allow Name Change", @@ -1572,6 +1573,7 @@ "Desktop_Notifications_Enabled": "Desktop Notifications are Enabled", "Desktop_Notifications_Not_Enabled": "Desktop Notifications are Not Enabled", "Unselected_by_default": "Unselected by default", + "Unseen_features": "Unseen features", "Details": "Details", "Device_Changes_Not_Available": "Device changes not available in this browser. For guaranteed availability, please use Rocket.Chat's official desktop app.", "Device_Changes_Not_Available_Insecure_Context": "Device changes are only available on secure contexts (e.g. https://)", @@ -2141,6 +2143,8 @@ "Favorite": "Favorite", "Favorite_Rooms": "Enable Favorite Rooms", "Favorites": "Favorites", + "Feature_preview": "Feature preview", + "Feature_preview_page_description": "Welcome to the features preview page! Here, you can enable the latest cutting-edge features that are currently under development and not yet officially released.\n\nPlease note that these configurations are still in the testing phase and may not be stable or fully functional.", "featured": "featured", "Featured": "Featured", "Feature_depends_on_selected_call_provider_to_be_enabled_from_administration_settings": "This feature depends on the above selected call provider to be enabled from the administration settings (Admin -> Video Conference).", @@ -3563,6 +3567,7 @@ "Name_of_agent": "Name of agent", "Name_optional": "Name (optional)", "Name_Placeholder": "Please enter your name...", + "Navigation": "Navigation", "Navigation_History": "Navigation History", "Next": "Next", "Never": "Never", @@ -4055,6 +4060,8 @@ "Queue_delay_timeout": "Queue processing delay timeout", "Queue_Time": "Queue Time", "Queue_management": "Queue Management", + "Quick_reactions": "Quick reactions", + "Quick_reactions_description": "The three most used reactions get an easy access while your mouse is over the message", "quote": "quote", "Quote": "Quote", "Random": "Random", @@ -4268,6 +4275,7 @@ "Required_action": "Required action", "Default_Referrer_Policy": "Default Referrer Policy", "Default_Referrer_Policy_Description": "This controls the 'referrer' header that's sent when requesting embedded media from other servers. For more information, refer to [this link from MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy). Remember, a full page refresh is required for this to take effect", + "No_feature_to_preview": "No feature to preview", "No_Referrer": "No Referrer", "No_Referrer_When_Downgrade": "No referrer when downgrade", "Notes": "Notes", diff --git a/apps/meteor/public/images/featurePreview/quick-reactions.png b/apps/meteor/public/images/featurePreview/quick-reactions.png new file mode 100644 index 0000000000000000000000000000000000000000..600636789c96bfa685d02e5f8bd63505b8f5b2cc GIT binary patch literal 5613 zcmchbXH=6-l*dDn9-0tBq)3sj36LOCq<0WOiuC>}Ql%F|?^0Ey!%LTr^b$zuAPPt? z5{h&PL3(F#ch8>vxS#gJGtd0z%(-{w&dfbCzv$=BRDcu^3IG5AR8v*d1po;4ZtJFG zB)4VKo%O!kAGxclkvjlDdH3%}07%aS-!>As>#E2DDu-A$Zw2uyIW0K=;A;ZqrPUn( zfH6c(QBKc?05^|JWbsTR-E%oPMC}gkIE&3B(#jL6%U1{O(Hodc#Rfr?d|N~5k94ug zpI`D7e=^?Scup!1u8;D0y-JfXdJr#9PtOoW#_@{-8oke%NGvZe=3={Gv5st+Ti>+$ zK+bJzRs8Ce$b$AnO|7VcmWy@+8ohpY)zXKNEEqd;j{`i#OYAD`|7M}3qx)F^qe5*E zLOaQ^;5f+Kv46Qom*@PXW5XU7(=OsZCud$dr(A$PoCWn07RzxpL+J`dxu5ZxGVz8+ zMMb^&V6d|EeD618xd^+YkEL??+rP*tYiKR7pfS!vLYh>0QR~)=4$?I)Ap=GvLr6zQ zHvyb-g^B<hhN7dQwqgk0VO3wbJ-Rjar84!ezm_flFDGZI0+s8nkm&yZ6`-hh%T14J z(u7$0f(09+qb{wDK=Mnpnq9K_mO&q(aMV}{JTD2BcN+|n^L);{tx-abk3TpH`!ZkF zS;=QU^w?K=l%XU9krM)0`P@yaIt2e_q}(qwLo0Uxcr6$x#5zZZZYKxA9dCXz`)krg z^JDU3I{gHL<+wU*1i>>!MZHMyqN1XrXViZQ9|-dV4=?F@#R9=G7={yt<=4W}GUZ8> z|G~84l#4KRoT(m_Tpt5iLhm93NW>=!E-Oi`fI^XUv0b}JvSXAa-RtboHv{q1_rcMa znag{rHf2GUk7K(v$-e_&qd?w!!K`R-@Qk6)dQR_bnH0v=aXP_*ygOcTu{<ZZ_m!<J zakL_#VkFV6Y3|2~K@LooVfkdlDv1F&14Zq&dlQMjP@r&WhefI?`9m(sV4JEBfE;K7 zvFD&ACPHVLq!v%3Dud*J9m#Wl%uyWM8Pj=qY!aw!xjW-38tO{25HxelF_JqUus&}d zwcxe=@CymD;?-0|-%wWT%JCN}w&j<um+w5Xzmh55?Ew_0+t%k0`uo%WqZt5Gpf9S* zGyV{2mU{fN%Ifq1(@!qR!Yyizo>rAS$uBAIK5+i}>?9HtPx(9*LHXmLQW5ViSPEI( z9kB?efd(hBhu0pc1rhA>-&u<QX1I2o<?MC3q5D)+o&#H0(Jkwbu_*!z@fp{@c~cSF zs-_1&To!9I9(CG|2KN|NwLoZOng~B?oupB_c`S^a!Z0N~X-g%pA+uW^o`jHw7g_G# zB3sQdn25-cuN(4k_F7^algUokP}PKjk=j+e6?b12PhH+TEe?i)xOtDmGx`mV9c&#5 ze=D>5(3!~P8h5MRjjEswT>m=6#B#c92{Z~T=esN-eF^;6d-U7>IeWlDD-!1&=!&^H zxfR1x$45Bt>vb$9({m-szswXEF_9p>JCAL4Xgty0x@M&DnX9q9zKkJ_h*)N{Fx|p^ zv_m%?1Wg=Y2^#p#UF^7Tzn#-|>9rm07^+{m=qPZG+^wx0mR@MRYFy7*h4+cSy+bv@ z&D;FKc*3{Y4l}^3L-sv5holi5n+qIF!oFjVH`-@x<|QiAD%&0`)W0I%VP@&A45o9$ z-;+DRz+z|K)8z#6e=jP*x-*%cB3=%pjG(U#!2wRd%ay~Y<h8%`ni)rusAD(TgU1dS zO0Bwj<IGQ!=b9Z-h}Wy$H?GQ-C!`p*_|3IycP`LCMhs1x$0=P{6{YIE5Ny|1ZsbGO zDz-;94yeiI0Q;9t(@g`d6+}tE46)o%I28hgzry=d&k9U^Ghq)rZuT@!)NZ&)k%5)W z>99=)TuDA*`AS%vzOrLPASiMNvS=(4UX)EHbJTVz^ExY#-n!T<O4eQTh3@W;ffM0^ z2B~<Nn`;hJ-yh`tE2ia_tLon~S(R*u`nVNVo0Z9e8@#wBpvo;S_i9=v^X;(Kr(NOp z8jLgJXUD@NvE6@QUQz-yS;Z-N@`ztv+wE_(6gFZ6r*J2yM>40^*lz|3$fF##NDJqd zvtsQ1<t5S-_UiP>YN7hlsw=Fg`JLS<mL`MA7MX10i(&<Xq?QWfqkyAr!{34$Xn2W! zaeL0}lb^wThM&(Dl>B&+^UF`)*PaKkgBSJODX!>AkZ4ll`0&QpLbPE|&Y8q#0{F$} zmLh2a*ZO=8Ck0&($>~@Qeu`QucUHSA$a+!kWHY%A-O-AIC*0>Iy?HSlqpm6wBX1f4 zcVi(m5ZyT>!@jI#tCE_JgD!jp-A$f5zfaf&#ncHkiG%|b+RN|9s^0y+S=^72TU*uP zd-?<%IrEfh_gHczP*X$FY;D8rh4Ia3TmL_njoI-d!Z|%lszfi!RHRQ+Xu#@?^%8~? z6yXzr#-W3c+Fqlbe7)wcLo+CcF1oR}pA^WBKYjja6-3AjQl=BA#i|znr{lOr*7qRa zQ1U8&+Jr&8t8i(Y`vRVwDDzE>^dZHs$JeYK-I<N}BtRoQ*=$Bqz}8rG(Zh)3)Q7n| zI8RZ|ij`qZto|B9rBT|euNTqW@o95$jw2oYi~kZYgFC$_48DaVH5S-A-FMzXhJt+* z*l60K6iq0<XU0|D1Tw3%=JORqSoRnT({Y)wD>d2hSgUw)(tc`odU361SIJ7y%sS#e zf<WPrHwuH%y3x?Y`Q*r@mBm@#=`^d@wrf7O40hSRCCK8S;Vx6^ENIHB{Sob0)q|YY zaY~(vrh1C0>T!W#C?rt=-*&R@x!vZ4d@>(#$Bu}?Y_Z6FDS))8q5nvO6xhNhH%0zw z<GR&xvIh$Z&Tzga2MLK^n^6L;dX*giMuSH`U~qU<Mt76M#EN*%@(>GMLhAi_=djw+ zpWxY*BpcKFfUklr&22Yw%Gg_^zs;4se2hJaIsIk$DuY{;gum<e9dS40Ja~(_dzM#M zwT>8O7|#od8fa(GysO%Vh+##Nn9qGy;t>t&$#GP$R%hX5VUo2SSGKD2?<BzbtpUM4 zo4nPeEhAD$HldR4Hm1i%A3q=drg!f_2I&r(8WSmR4l0@p{8=&^){x6q?wgr-V)sVm zwI%lAhyU^Q&O=>=x8~Qfb^16aAUiO3-%`kBfjyR*Pg(3-5^rL>=#6*5`wqXD_87@W zM>ZUJ#<v?=eN%E|*X$zp;btM$AD*5T&mO-aE7wVDMHC=1DOIEXDNN69hoTUlcIeU! zvGHG6R#?a%j7>IJd^btk1t}+P%}0b~C0z*YkwOXVji@6ooTR==L^go#y03?g)Xyoa zo9Ed~E6IvEZD#{^P+V5*7}*4=lLWPQJD0GK2;kh?LP^*kZG{@^v5|nSBe2iWI#}IV z>GF#xZA`tYDQVl)YK6?inKP0f=!AdZR3)0kh7HaA;b0ja-y16$C={Cgt>T$>_xlUq zp-7O=g6GedzJ&%7d!MO$A?5-4pZ!J*SA$C?sv`XCGvHzHM3B&QW1KPWO>+AVlXvci zZziLwb6;?O+|27uKwA<UsR#$2%RI^xI40$4zcO9=<}&O3G<flM7eyw|aQ}fwaVvh& zVfX5EuaRhEpi>Q}JUsbyg(6h7E}ed*O=O)#mM2YR!GymKdMRO?&zj0NM$r-Hai1R2 z{X(1CQo~ym#6zN!7~-u{JQNs78_!4zdM`5{v-6s*wM7K{!}jZ$7G;OZ@#6QpHL7dC zu-RAn^n|$}O%os1h{j83#N&koes>A09=pIa$ntryNhyZaiOvUi(5cEq^7v`=#^JEd zHxx8)6i!wjGuCVll*<~Pcu`arSAm+CLbvr%ssVv*7A7?imH<O{h~D}6Du=$GmYHI4 zZl#QaXFxRtl9QT2Vpe_%`E}ty%=yDr!?b^UM6$Dq?_Arc$IbS|zj%GM5;L<|nidxi z|3m390u@A=OV91@p}IXc(P19Weg03&r{o%IZK57%!j0&-ix6D2l7y8%xjF1@wjvAL zvi&p#4qP-&wae`)%F|wTv6_VUWZhXwxKQ~nj&ld^Zg<9K&#R&<Q?#}Ge3{7*T;t<0 z|CFT1i~EZ02VQYKMO;nwQ%k67oAD*|J%2)C9DhZq+d-RIX@QYG*8?vx*U*V1IBb|U zZEHPfivQlXz7R4t7tVix4t?EcwD;Zs)k3B|S5dgNpI(Okdh!<d+>EL)H+RC$Uf-*y zQGrmI=Gadl6+7u9@%(f{s=4y%$y&#10B~HFH}?blo%)>4mIO;_c;zs1{oni#C?$`Y z8}{oZ3TU0@&BmezUa%Oiz4^<dVth~%G|}0NKS>~X_Oa`k#@>c6cCA$#IlAX%7eDpP z54RyZB7g8HO6rFa-wKAx;q?;{Xtol^3{k!d5vS;Iw_<(v{)58`)A-G3DeHN3*YPXe z)VIG>gad#2rb^g=gvfQWJ`NXr0B_94v1VlVH5B6&9Nr+x?Tx5nC0=iuQpkjHj@i2Y z1bTaf?xa5=ciqO7kT~Tq#az@R2B+<YGunsdIHSv&_%uS*OjaSWtE3B~XWi#bB+Vpk z7NoB#SOSkM(0@ubkgU>ubK7L5Zb?(0C#y#(8ZS#<LCnu<*$)vOl}6}<;t%7M!J~VJ zze^qOiM1PK+NA86a~{kZdv0&v-UZ80$YPmo|1TGcOn|Kb{;Kh^==alD8LkT61~I@; zu%V&F4Q){aT1DR!OlXdVV2^q<a=sMiRO$=0wUz2JHLmuZ3y;o+RvIeP+PhhW=a0%! ze>1JyHWNg?S@%wSd1|H?7Iv`hogK<^U>9bb@3WI8%s_ExWktK&ix5U+<r_oIB0{F_ zMG`8)#adH+ddM^LizOnmXldHz>W)F({)E}C%j3?SGem5>K?$Y{`>w5GJ!tUX-gg&o zJZrwXDi=a>{x}i8x}m)MSI!;hU3DO!?1bLx(RIGqY*%tC>`^VGct+D7ptta#$9*Ja zw^3Z9QAfcHdOx3|ka9{rK|fy!wbH54=1D%d+wNdmr8CO|oK?3Nt*hHNwKtRvZGC5{ z<A*+cI<Fl7bfhl)@P7JmdhFM)FlEebmQnfFNM5DXmHU0#L&kzK_L&mFih6C_!|$V8 zlChTDVKbEUQN3Mc4`vI^*v2;YAF;oA9IhigN}h^56G1iau#eF9s|!j8BZjHCXg_~} zd$=Y&lGb^wC9r>g$c!N}W609dn?w|f9!uFXaK^j4+O^NKvIO0OC4H9bc{%zg1Z7kN zwjND}6$lbApNL-#1D@V#GWR>I(TG_ma}c~rFRPlx|FI5l#IslfsbuV74lw&d3yRgy z`U=k>^RAsN)=>4aj&7~TH(-sY-sF$OZ=|4L8ZY(F23Zo*YEqyS+Bw`@apC6uvbtwW zvN8H+D`AbxoDDP)jN)g@iHuEtxP{w?Qe?((ZsOp51bjn(+KO2^!|xBMtf|zguJK;D zhkA%q)XO<I6oYWgGNotU_JHT;3~2Ma3)gJE-D$y7zIGnSD?x;0g_5*jzj3Assk*jC z@ph&PKL~0b)3Q|-0q+jv<9?aAc3b24^scwMpS9d;)-N7N<%!IUb?T)c>D>pV$6ti# zEio5(ONBKh%W3pStU~z0iaGqQB{r+4Ft0uvs-2xJ^c<hH2OU2$udz<{?qD)QSX2nc zF+xRNphPmf=Z|V=r*?xHqcjDyk>yRsHyVuWFMS*sR>%UY_bLl%MEQ5$M?<Y03MeuO zp0$IugCvP=B3H&Vk|1o!RV*a_rc_56;<=)+YsX>4O<_&UvdYw6_im(uRBHv7*ggwL z1LnMh?XHVqxJ(nvP2b<i+)n$B_<tPR)+vbHtJGFqI9P!Fi<~R<<p)OgWv{p;R90Jk z8U8W9bmxx0@R`|kNlwQ35tmHd#Kv9@@jnj2(DCN|F3+Ib7XRV@&SMzjakC0aMONhx z3=FXNko}=F;DCE_K-BE%Ca(>x1+z0Ml3Topcz(kL=Gn5GN6yYhJ=@>QF`iTg^*&kn zW*x*H0bZOyR8pA6IyNy$PDvA%U2)e9Ni#pJl?ll;WXEi1$SA<a1@#;G-G{^+OC(H+ z8aWtT4g_N4#el`X)!{iFtd~oEa@dnjbt<$g)NOt^AJ)=3Jx|lfA#wn(%l08J5<=-o z5c*Z`;oXDJr>->HokKasNO8sQh@2>|^+|*;Zu}>G&#vPJM^2_%mty0V`ww<yw^4&5 zQ79UrnYy?5VXA@NPJj%RFs94_MGycx8>jU%zvODMxD5S%<;4YJQPjB9f@`Z`-kNep zx~Y0#<4-RKm!K!O9?!kCUuJy~B`IPPnB%qWs~cJLuYRvsoDcCs_H*8}gg`tpU3<58 zFC(Gfa-+67ohKkbN~g{0zk~7<Ad`sF!!%!4VEOfyj(w}Gud^R1DIP_ZjYPkB4tqLw zaf@``9NzqeJ<I<?Ioe@IQclGjpABhs;zqXNaC#E)v(Ag|YDbWw;>3p^YqS)Ip%A-A z)@AouFIOq=;4f@riM7ULM{2M~GxtfC{c=c`PaCp+s)F-v_-8;wba+C(-_v}Yc{H*K zHk+EDO%IS$#oLvAF$jS+R~hY(4%7DMcOPa}$I8+qr})ko%&mhOxh^<)^?h<jz=4{I z@F#V$)dky3F7av{HtHwPgxj=*l8;IDc8U%6Fp=*#32?v(eAnKV^5Gs8pZd*~paA6F zUcz)a2+&TD+~$7OhYzQhmxVd=x^A!Aw-con#{30cwEtIul1oF0`A%%sZ_%7(LIN#% z4<u=IJoj`<fdULQmUGX#ke687pALuw-pe;<NM<}<R&k;O;9?mJxCg6`!c)@4ktQ(w zFM#wvasFRuschulr|iNNajCd+<CtR0H1TnJY_g9<>6!6e9q62qU~en>%Uc-co?h<r zARG);HUMW03&CIU8kU?CrdJ{8j%u>%t)(vBLr6_FfH9=qTkgGIhXs$?^$zNJ^2{4* z4a9#j=_wPvyZ3jco%^5fDvfzoQ``<p?=fKnag!BTQ10&tyWWO8PXGDZwL)*nJLtCS zo9wz_e3WvW6PB~V5$+SpapmzB9{vz1@MHs$y6icp?6P>wPr6D3hFUF~{3o1T*2mIg zMUJEkWYF6m0Gl3u@k9p^K~|aE+E1BpUBHbR)Ie1trZ$Dod{bohym{f9Kd7XhncdP^ OfSS@X#Y%b0kpBSo`qq^I literal 0 HcmV?d00001 diff --git a/apps/meteor/server/settings/accounts.ts b/apps/meteor/server/settings/accounts.ts index 586795cef638..c3273c282aa8 100644 --- a/apps/meteor/server/settings/accounts.ts +++ b/apps/meteor/server/settings/accounts.ts @@ -205,6 +205,10 @@ export const createAccountSettings = () => type: 'boolean', public: true, }); + await this.add('Accounts_AllowFeaturePreview', false, { + type: 'boolean', + public: true, + }); await this.add('Accounts_CustomFieldsToShowInUserInfo', '', { type: 'string', public: true, diff --git a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts index d820dfe5cb2d..ce1e33e02dc6 100644 --- a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts +++ b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts @@ -39,6 +39,7 @@ export type UsersSetPreferencesParamsPOST = { sidebarGroupByType?: boolean; muteFocusedConversations?: boolean; dontAskAgainList?: Array<{ action: string; label: string }>; + featuresPreview?: { name: string; value: boolean }[]; themeAppearence?: 'auto' | 'light' | 'dark'; receiveLoginDetectionEmail?: boolean; notifyCalendarEvents?: boolean; @@ -197,6 +198,17 @@ const UsersSetPreferencesParamsPostSchema = { }, nullable: true, }, + featuresPreview: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + value: { type: 'boolean' }, + }, + }, + nullable: true, + }, themeAppearence: { type: 'string', nullable: true, diff --git a/yarn.lock b/yarn.lock index 76c68d5fd8dd..7bc670d2dd2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9392,7 +9392,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.12, @rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.151": +"@rocket.chat/css-in-js@npm:^0.31.12, @rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.154": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9418,7 +9418,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.151": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.154": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9646,10 +9646,10 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.327": - version: 0.32.0-dev.327 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.327" - checksum: 93597b59a76829d5074834295c0b13ecf1256433bdedab6ce5f92b50799893ca8f5eceabcdeaba0fbd24df7abd64f4c62fa28f008d8fa90a7ecd869bbd8b0d6a +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.330": + version: 0.32.0-dev.330 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.330" + checksum: 9733eeb0c8f1d8220e13d20c85e9ecf219b6d22e3c339352a080a19cbdc7aae7828ae591884b070c48047fa984383122d8a816e7102a4c38ba6a6ce73f364452 languageName: node linkType: hard @@ -9709,14 +9709,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.377 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.377" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.151 - "@rocket.chat/css-supports": ~0.31.23-dev.151 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.327 - "@rocket.chat/memo": ~0.31.23-dev.151 - "@rocket.chat/styled": ~0.31.23-dev.151 + version: 0.32.0-dev.380 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.380" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.154 + "@rocket.chat/css-supports": ~0.31.23-dev.154 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.330 + "@rocket.chat/memo": ~0.31.23-dev.154 + "@rocket.chat/styled": ~0.31.23-dev.154 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9728,7 +9728,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: e61ddd6ce6dd7ea4ba8d5b5c5f464a82d5600934c7ae05a38f20227308f39b55dbd5b91f9190d494b814efac6e60eb70810ab7d171ff1b482e54a2132573bdda + checksum: 07be707ad2fab881852d1b4ac49044915b24d5bf82352cc6e3e57526dfbcd13ec3b2d73b362bbe52d122aa5b45d90371838f1e463a443934d573022ac70a57e6 languageName: node linkType: hard @@ -9816,9 +9816,9 @@ __metadata: linkType: soft "@rocket.chat/icons@npm:next": - version: 0.32.0-dev.349 - resolution: "@rocket.chat/icons@npm:0.32.0-dev.349" - checksum: 45553695c95ef0aa908b133e7347aff9b3845fe03486e145ed2e717efa1b2fc5044f8d7bd390031cc3e401ecfb89723ef4c158a136f164bf73b4a9653775963d + version: 0.32.0-dev.362 + resolution: "@rocket.chat/icons@npm:0.32.0-dev.362" + checksum: 1c2096913e154d0db68b8ccc933294486ff06e250983809ce165f1497df05deec8b5165b47dbefdd013013b4cdf4a3a20f2ac191155b629e5ec4a7bcab5d8870 languageName: node linkType: hard @@ -9956,7 +9956,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.151": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.154": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -10761,7 +10761,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.151": +"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.154": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: From 7a829e05113278bf3495ed154f05cb47ad5273de Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Thu, 6 Jul 2023 18:57:37 -0300 Subject: [PATCH 083/149] ci: create reporter (#29662) --- .github/workflows/ci-test-e2e.yml | 9 ++++++ .github/workflows/ci.yml | 4 +++ apps/meteor/playwright.config.ts | 12 ++++++-- apps/meteor/reporters/rocketchat.ts | 47 +++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 apps/meteor/reporters/rocketchat.ts diff --git a/.github/workflows/ci-test-e2e.yml b/.github/workflows/ci-test-e2e.yml index a27915154277..28cbdf0ce4e7 100644 --- a/.github/workflows/ci-test-e2e.yml +++ b/.github/workflows/ci-test-e2e.yml @@ -56,6 +56,10 @@ on: required: true QASE_API_TOKEN: required: false + REPORTER_ROCKETCHAT_URL: + required: false + REPORTER_ROCKETCHAT_API_KEY: + required: false env: MONGO_URL: mongodb://localhost:27017/rocketchat?replicaSet=rs0&directConnection=true @@ -243,6 +247,11 @@ jobs: env: E2E_COVERAGE: ${{ inputs.release == 'ee' && 'true' || '' }} IS_EE: ${{ inputs.release == 'ee' && 'true' || '' }} + REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} + REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} + REPORTER_ROCKETCHAT_REPORT: ${{ github.ref == 'refs/heads/develop' && 'true' || '' }} + REPORTER_ROCKETCHAT_BRANCH: ${{ github.ref }} + REPORTER_ROCKETCHAT_DRAFT: ${{ github.event.pull_request.draft }} QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} QASE_REPORT: ${{ github.ref == 'refs/heads/develop' && 'true' || '' }} working-directory: ./apps/meteor diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c71965aa907..1452e5b122d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -259,6 +259,8 @@ jobs: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} + REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} + REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} test-api-ee: name: 🔨 Test API (EE) @@ -307,6 +309,8 @@ jobs: CR_USER: ${{ secrets.CR_USER }} CR_PAT: ${{ secrets.CR_PAT }} QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }} + REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }} + REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }} tests-done: name: ✅ Tests Done diff --git a/apps/meteor/playwright.config.ts b/apps/meteor/playwright.config.ts index cc846051a5a7..aaedd42e78d6 100644 --- a/apps/meteor/playwright.config.ts +++ b/apps/meteor/playwright.config.ts @@ -22,7 +22,15 @@ export default { outputDir: 'tests/e2e/.playwright', reporter: [ ['list'], - // process.env.CI ? ['github'] : ['list'], + process.env.REPORTER_ROCKETCHAT_REPORT === 'true' && [ + './reporters/rocketchat.ts', + { + url: process.env.REPORTER_ROCKETCHAT_URL, + apiKey: process.env.REPORTER_ROCKETCHAT_API_KEY, + branch: process.env.REPORTER_ROCKETCHAT_BRANCH, + draft: process.env.REPORTER_ROCKETCHAT_DRAFT === 'true', + }, + ], [ 'playwright-qase-reporter', { @@ -36,7 +44,7 @@ export default { environmentId: '1', }, ], - ], + ].filter(Boolean), testDir: 'tests/e2e', testIgnore: 'tests/e2e/federation/**', workers: 1, diff --git a/apps/meteor/reporters/rocketchat.ts b/apps/meteor/reporters/rocketchat.ts new file mode 100644 index 000000000000..c4ef26acf049 --- /dev/null +++ b/apps/meteor/reporters/rocketchat.ts @@ -0,0 +1,47 @@ +import fetch from 'node-fetch'; +import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter'; + +class RocketChatReporter implements Reporter { + private url: string; + + private apiKey: string; + + private branch: string; + + private draft: boolean; + + constructor(options: { url: string; apiKey: string; branch: string; draft: boolean }) { + this.url = options.url; + this.apiKey = options.apiKey; + this.branch = options.branch; + this.draft = options.draft; + } + + onTestEnd(test: TestCase, result: TestResult) { + if (process.env.REPORTER_ROCKETCHAT_REPORT !== 'true') { + console.log('REPORTER_ROCKETCHAT_REPORT is not true, skipping', { + draft: this.draft, + branch: this.branch, + }); + return; + } + const payload = { + name: test.title, + status: result.status, + duration: result.duration, + branch: this.branch, + draft: this.draft, + }; + console.log(`Sending test result to Rocket.Chat: ${JSON.stringify(payload)}`); + return fetch(this.url, { + method: 'POST', + body: JSON.stringify(payload), + headers: { + 'Content-Type': 'application/json', + 'X-Api-Key': this.apiKey, + }, + }); + } +} + +export default RocketChatReporter; From b6a2c7763a0e86def9b79d4921fadc267512c0e7 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Thu, 6 Jul 2023 18:57:48 -0300 Subject: [PATCH 084/149] chore: improve server error handling (#29745) --- .../server/lib/RocketChat.ErrorHandler.ts | 118 +++++++++++------- apps/meteor/app/lib/server/lib/meteorFixes.js | 45 +------ 2 files changed, 74 insertions(+), 89 deletions(-) diff --git a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts index 0aa8c3a53747..e4a549a1f02d 100644 --- a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts +++ b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts @@ -20,55 +20,15 @@ class ErrorHandler { this.reporting = false; this.rid = null; this.lastError = null; - - Meteor.startup(async () => { - await this.registerHandlers(); - - settings.watch<string>('Log_Exceptions_to_Channel', async (value) => { - this.rid = null; - const roomName = value.trim(); - if (roomName) { - const rid = await this.getRoomId(roomName); - if (rid) { - this.rid = rid; - } - } - - if (this.rid) { - this.reporting = true; - } else { - this.reporting = false; - } - }); - }); - } - - async registerHandlers() { - process.on('uncaughtException', async (error) => { - incException(); - if (!this.reporting) { - return; - } - await this.trackError(error.message, error.stack); - }); - - // eslint-disable-next-line @typescript-eslint/no-this-alias - const self = this; - const originalMeteorDebug = Meteor._debug; - Meteor._debug = function (message, stack, ...args) { - if (!self.reporting) { - return originalMeteorDebug.call(this, message, stack); - } - void self.trackError(message, stack); - return originalMeteorDebug.apply(this, [message, stack, ...args]); - }; } - async getRoomId(roomName: string): Promise<string | undefined> { - roomName = roomName.replace('#', ''); - const room = await Rooms.findOneByName(roomName, { projection: { _id: 1, t: 1 } }); + async getRoomId(roomName: string): Promise<string | null> { + if (!roomName) { + return null; + } + const room = await Rooms.findOneByName(roomName.replace('#', ''), { projection: { _id: 1, t: 1 } }); if (!room || (room.t !== 'c' && room.t !== 'p')) { - return; + return null; } return room._id; } @@ -88,4 +48,68 @@ class ErrorHandler { } } -export default new ErrorHandler(); +const errorHandler = new ErrorHandler(); + +Meteor.startup(async () => { + settings.watch<string>('Log_Exceptions_to_Channel', async (value) => { + errorHandler.rid = null; + const roomName = value.trim(); + + const rid = await errorHandler.getRoomId(roomName); + + errorHandler.reporting = Boolean(rid); + errorHandler.rid = rid; + }); +}); + +// eslint-disable-next-line @typescript-eslint/no-this-alias +const originalMeteorDebug = Meteor._debug; + +Meteor._debug = function (message, stack, ...args) { + if (!errorHandler.reporting) { + return originalMeteorDebug.call(this, message, stack); + } + void errorHandler.trackError(message, stack); + return originalMeteorDebug.apply(this, [message, stack, ...args]); +}; + +/** + * If some promise is rejected and doesn't have a catch (unhandledRejection) it may cause this finally + * here https://github.com/meteor/meteor/blob/be6e529a739f47446950e045f4547ee60e5de7ae/packages/mongo/oplog_tailing.js#L348 + * to not be executed never ending the oplog worker and freezing the entire process. + * + * The only way to release the process is executing the following code via inspect: + * MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle._workerActive = false + * + * Since unhandled rejections are deprecated in NodeJS: + * (node:83382) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections + * that are not handled will terminate the Node.js process with a non-zero exit code. + * we will start respecting this and exit the process to prevent these kind of problems. + */ + +process.on('unhandledRejection', (error) => { + incException(); + + if (error instanceof Error) { + void errorHandler.trackError(error.message, error.stack); + } + + console.error('=== UnHandledPromiseRejection ==='); + console.error(error); + console.error('---------------------------------'); + console.error('Errors like this can cause oplog processing errors.'); + console.error( + 'Setting EXIT_UNHANDLEDPROMISEREJECTION will cause the process to exit allowing your service to automatically restart the process', + ); + console.error('Future node.js versions will automatically exit the process'); + console.error('================================='); + + if (process.env.NODE_ENV === 'development' || process.env.EXIT_UNHANDLEDPROMISEREJECTION) { + process.exit(1); + } +}); + +process.on('uncaughtException', async (error) => { + incException(); + void errorHandler.trackError(error.message, error.stack); +}); diff --git a/apps/meteor/app/lib/server/lib/meteorFixes.js b/apps/meteor/app/lib/server/lib/meteorFixes.js index 39616cac4042..dd0764c46826 100644 --- a/apps/meteor/app/lib/server/lib/meteorFixes.js +++ b/apps/meteor/app/lib/server/lib/meteorFixes.js @@ -1,7 +1,4 @@ import { MongoInternals } from 'meteor/mongo'; -import { Settings } from '@rocket.chat/models'; - -import { throttledCounter } from '../../../../lib/utils/throttledCounter'; const timeoutQuery = parseInt(process.env.OBSERVERS_CHECK_TIMEOUT) || 2 * 60 * 1000; const interval = parseInt(process.env.OBSERVERS_CHECK_INTERVAL) || 60 * 1000; @@ -19,16 +16,16 @@ const debug = Boolean(process.env.OBSERVERS_CHECK_DEBUG); * A good way to freeze a observer is running the instance with --inspect and execute in inspector the following code: * multiplexer = Object.values(MongoInternals.defaultRemoteCollectionDriver().mongo._observeMultiplexers)[0] * multiplexer._observeDriver._needToPollQuery() - * Whis will raise an error of bindEnvironment and block the observer + * This will raise an error of bindEnvironment and block the observer * here https://github.com/meteor/meteor/blob/be6e529a739f47446950e045f4547ee60e5de7ae/packages/mongo/oplog_observe_driver.js#L698 * - * This code will check for observer instances in QUERYING mode for more than 2 minutues and will manually set them back + * This code will check for observer instances in QUERYING mode for more than 2 minutes and will manually set them back * to STEADY and force the query again to refresh the data and flush the _writesToCommitWhenWeReachSteady callbacks. */ setInterval(() => { if (debug) { - console.log('Checking for stucked observers'); + console.log('Checking for stuck observers'); } const now = Date.now(); const driver = MongoInternals.defaultRemoteCollectionDriver(); @@ -45,39 +42,3 @@ setInterval(() => { _observeDriver._needToPollQuery(); }); }, interval); - -const incException = throttledCounter((counter) => { - Settings.incrementValueById('Uncaught_Exceptions_Count', counter).catch(console.error); -}, 10000); - -/** - * If some promise is rejected and doesn't have a catch (unhandledRejection) it may cause this finally - * here https://github.com/meteor/meteor/blob/be6e529a739f47446950e045f4547ee60e5de7ae/packages/mongo/oplog_tailing.js#L348 - * to not be executed never ending the oplog worker and freezing the entire process. - * - * The only way to release the process is executing the following code via inspect: - * MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle._workerActive = false - * - * Since unhandled rejections are deprecated in NodeJS: - * (node:83382) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections - * that are not handled will terminate the Node.js process with a non-zero exit code. - * we will start respecting this and exit the process to prevent these kind of problems. - */ - -process.on('unhandledRejection', (error) => { - incException(); - - console.error('=== UnHandledPromiseRejection ==='); - console.error(error); - console.error('---------------------------------'); - console.error('Errors like this can cause oplog processing errors.'); - console.error( - 'Setting EXIT_UNHANDLEDPROMISEREJECTION will cause the process to exit allowing your service to automatically restart the process', - ); - console.error('Future node.js versions will automatically exit the process'); - console.error('================================='); - - if (process.env.NODE_ENV === 'development' || process.env.EXIT_UNHANDLEDPROMISEREJECTION) { - process.exit(1); - } -}); From e1e77d7fde3e344058873de7fc3f0ecaeffd7c40 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Thu, 6 Jul 2023 18:58:08 -0300 Subject: [PATCH 085/149] test: remove tests that were not unit tests (#29734) --- .github/workflows/ci-test-unit.yml | 17 +- .../app/models/server/raw/Sessions.tests.js | 1154 ----------------- 2 files changed, 1 insertion(+), 1170 deletions(-) delete mode 100644 apps/meteor/tests/unit/app/models/server/raw/Sessions.tests.js diff --git a/.github/workflows/ci-test-unit.yml b/.github/workflows/ci-test-unit.yml index e38288e90617..f294ac2dda7b 100644 --- a/.github/workflows/ci-test-unit.yml +++ b/.github/workflows/ci-test-unit.yml @@ -6,10 +6,6 @@ on: node-version: required: true type: string - mongodb-version: - default: "['4.4', '6.0']" - required: false - type: string env: MONGO_URL: mongodb://localhost:27017/rocketchat?replicaSet=rs0&directConnection=true @@ -19,20 +15,9 @@ jobs: test: runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - mongodb-version: ${{ fromJSON(inputs.mongodb-version) }} - - name: MongoDB ${{ matrix.mongodb-version }} + name: Unit Tests steps: - - name: Launch MongoDB - uses: supercharge/mongodb-github-action@1.9.0 - with: - mongodb-version: ${{ matrix.mongodb-version }} - mongodb-replica-set: rs0 - - uses: actions/checkout@v3 - name: Setup NodeJS diff --git a/apps/meteor/tests/unit/app/models/server/raw/Sessions.tests.js b/apps/meteor/tests/unit/app/models/server/raw/Sessions.tests.js deleted file mode 100644 index 05d26da43db4..000000000000 --- a/apps/meteor/tests/unit/app/models/server/raw/Sessions.tests.js +++ /dev/null @@ -1,1154 +0,0 @@ -import { expect } from 'chai'; -import { MongoMemoryServer } from 'mongodb-memory-server'; - -const { MongoClient } = require('mongodb'); - -const { aggregates } = require('../../../../../../server/models/raw/Sessions'); - -const sessions_dates = []; -const baseDate = new Date(2018, 6, 1); - -for (let index = 0; index < 365; index++) { - sessions_dates.push({ - _id: `${baseDate.getFullYear()}-${baseDate.getMonth() + 1}-${baseDate.getDate()}`, - year: baseDate.getFullYear(), - month: baseDate.getMonth() + 1, - day: baseDate.getDate(), - }); - baseDate.setDate(baseDate.getDate() + 1); -} - -const DATA = { - sessions: [ - { - _id: 'fNFyFcjszvoN6Grip2', - day: 30, - instanceId: 'HvbqxukP8E65LAGMY', - month: 4, - sessionId: 'kiA4xX33AyzPgpBNs2', - year: 2019, - _updatedAt: new Date('2019-04-30T16:33:24.311Z'), - createdAt: new Date('2019-04-30T00:11:34.047Z'), - device: { - type: 'browser', - name: 'Firefox', - longVersion: '66.0.3', - os: { - name: 'Linux', - version: '12', - }, - version: '66.0.3', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-04-30T00:11:34.047Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - lastActivityAt: new Date('2019-04-30T00:16:20.349Z'), - closedAt: new Date('2019-04-30T00:16:20.349Z'), - }, - { - _id: 'fNFyFcjszvoN6Grip', - day: 2, - instanceId: 'HvbqxukP8E65LAGMY', - month: 5, - sessionId: 'kiA4xX33AyzPgpBNs', - year: 2019, - _updatedAt: new Date('2019-05-06T16:33:24.311Z'), - createdAt: new Date('2019-05-03T00:11:34.047Z'), - device: { - type: 'browser', - name: 'Firefox', - longVersion: '66.0.3', - os: { - name: 'Linux', - version: '12', - }, - version: '66.0.3', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-05-03T00:11:34.047Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - lastActivityAt: new Date('2019-05-03T00:16:20.349Z'), - closedAt: new Date('2019-05-03T00:16:20.349Z'), - }, - { - _id: 'oZMkfR3gFB6kuKDK2', - day: 2, - instanceId: 'HvbqxukP8E65LAGMY', - month: 5, - sessionId: 'i8uJFekr9np4x88kS', - year: 2019, - _updatedAt: new Date('2019-05-06T16:33:24.311Z'), - createdAt: new Date('2019-05-03T00:16:21.847Z'), - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-05-03T00:16:21.846Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - lastActivityAt: new Date('2019-05-03T00:17:21.081Z'), - closedAt: new Date('2019-05-03T00:17:21.081Z'), - }, - { - _id: 'ABXKoXKTZpPpzLjKd', - day: 2, - instanceId: 'HvbqxukP8E65LAGMY', - month: 5, - sessionId: 'T8MB28cpx2ZjfEDXr', - year: 2019, - _updatedAt: new Date('2019-05-06T16:33:24.311Z'), - createdAt: new Date('2019-05-03T00:17:22.375Z'), - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-05-03T00:17:22.375Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - lastActivityAt: new Date('2019-05-03T01:48:31.695Z'), - closedAt: new Date('2019-05-03T01:48:31.695Z'), - }, - { - _id: 's4ucvvcfBjnTEtYEb', - day: 2, - instanceId: 'HvbqxukP8E65LAGMY', - month: 5, - sessionId: '8mHbJJypgeRG27TYF', - year: 2019, - _updatedAt: new Date('2019-05-06T16:33:24.311Z'), - createdAt: new Date('2019-05-03T01:48:43.521Z'), - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-05-03T01:48:43.521Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - closedAt: new Date('2019-05-03T01:48:43.761Z'), - lastActivityAt: new Date('2019-05-03T01:48:43.761Z'), - }, - { - _id: 'MDs9SzQKmwaDmXL8s', - day: 2, - instanceId: 'HvbqxukP8E65LAGMY', - month: 5, - sessionId: 'GmoBDPKy9RW2eXdCG', - year: 2019, - _updatedAt: new Date('2019-05-06T16:33:24.311Z'), - createdAt: new Date('2019-05-03T01:48:45.064Z'), - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-05-03T01:48:45.064Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - }, - { - _id: 'CJwfxASo62FHDgqog', - day: 2, - instanceId: 'Nmwo2ttFeWZSrowNh', - month: 5, - sessionId: 'LMrrL4sbpNMLWYomA', - year: 2019, - _updatedAt: new Date('2019-05-06T16:33:24.311Z'), - createdAt: new Date('2019-05-03T01:50:31.098Z'), - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-05-03T01:50:31.092Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - closedAt: new Date('2019-05-03T01:50:31.355Z'), - lastActivityAt: new Date('2019-05-03T01:50:31.355Z'), - }, - { - _id: 'iGAcPobWfTQtN6s4K', - day: 1, - instanceId: 'Nmwo2ttFeWZSrowNh', - month: 5, - sessionId: 'AsbjZRLNQMqfbyYFS', - year: 2019, - _updatedAt: new Date('2019-05-06T16:33:24.311Z'), - createdAt: new Date('2019-05-03T01:50:32.765Z'), - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - host: 'localhost:3000', - ip: '127.0.0.1', - loginAt: new Date('2019-05-03T01:50:32.765Z'), - type: 'session', - userId: 'xPZXw9xqM3kKshsse2', - mostImportantRole: 'admin', - lastActivityAt: new Date('2019-05-03T02:59:59.999Z'), - }, - ], - sessions_dates, -}; - -describe('Sessions Aggregates', () => { - let db; - - if (!process.env.MONGO_URL) { - let mongod; - before(async function () { - this.timeout(120000); - const version = '5.0.0'; - console.log(`Starting mongo version ${version}`); - mongod = await MongoMemoryServer.create({ binary: { version } }); - process.env.MONGO_URL = await mongod.getUri(); - }); - - after(async () => { - await mongod.stop(); - }); - } - - before(async () => { - console.log(`Connecting to mongo at ${process.env.MONGO_URL}`); - const client = await MongoClient.connect(process.env.MONGO_URL, { - useUnifiedTopology: true, - useNewUrlParser: true, - }); - db = client.db('test'); - - after(() => { - client.close(); - }); - - await db.dropDatabase(); - - const sessions = db.collection('sessions'); - const sessions_dates = db.collection('sessions_dates'); - - return Promise.all([sessions.insertMany(DATA.sessions), sessions_dates.insertMany(DATA.sessions_dates)]); - }); - - it('should have sessions_dates data saved', () => { - const collection = db.collection('sessions_dates'); - return collection - .find() - .toArray() - .then((docs) => expect(docs.length).to.be.equal(DATA.sessions_dates.length)); - }); - - it('should match sessions between 2018-12-11 and 2019-1-10', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 1, day: 10 }); - - expect($match).to.be.deep.equal({ - $and: [ - { - $or: [{ year: { $gt: 2018 } }, { year: 2018, month: { $gt: 12 } }, { year: 2018, month: 12, day: { $gte: 11 } }], - }, - { - $or: [{ year: { $lt: 2019 } }, { year: 2019, month: { $lt: 1 } }, { year: 2019, month: 1, day: { $lte: 10 } }], - }, - ], - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(31); - expect(docs).to.be.deep.equal([ - { _id: '2018-12-11', year: 2018, month: 12, day: 11 }, - { _id: '2018-12-12', year: 2018, month: 12, day: 12 }, - { _id: '2018-12-13', year: 2018, month: 12, day: 13 }, - { _id: '2018-12-14', year: 2018, month: 12, day: 14 }, - { _id: '2018-12-15', year: 2018, month: 12, day: 15 }, - { _id: '2018-12-16', year: 2018, month: 12, day: 16 }, - { _id: '2018-12-17', year: 2018, month: 12, day: 17 }, - { _id: '2018-12-18', year: 2018, month: 12, day: 18 }, - { _id: '2018-12-19', year: 2018, month: 12, day: 19 }, - { _id: '2018-12-20', year: 2018, month: 12, day: 20 }, - { _id: '2018-12-21', year: 2018, month: 12, day: 21 }, - { _id: '2018-12-22', year: 2018, month: 12, day: 22 }, - { _id: '2018-12-23', year: 2018, month: 12, day: 23 }, - { _id: '2018-12-24', year: 2018, month: 12, day: 24 }, - { _id: '2018-12-25', year: 2018, month: 12, day: 25 }, - { _id: '2018-12-26', year: 2018, month: 12, day: 26 }, - { _id: '2018-12-27', year: 2018, month: 12, day: 27 }, - { _id: '2018-12-28', year: 2018, month: 12, day: 28 }, - { _id: '2018-12-29', year: 2018, month: 12, day: 29 }, - { _id: '2018-12-30', year: 2018, month: 12, day: 30 }, - { _id: '2018-12-31', year: 2018, month: 12, day: 31 }, - { _id: '2019-1-1', year: 2019, month: 1, day: 1 }, - { _id: '2019-1-2', year: 2019, month: 1, day: 2 }, - { _id: '2019-1-3', year: 2019, month: 1, day: 3 }, - { _id: '2019-1-4', year: 2019, month: 1, day: 4 }, - { _id: '2019-1-5', year: 2019, month: 1, day: 5 }, - { _id: '2019-1-6', year: 2019, month: 1, day: 6 }, - { _id: '2019-1-7', year: 2019, month: 1, day: 7 }, - { _id: '2019-1-8', year: 2019, month: 1, day: 8 }, - { _id: '2019-1-9', year: 2019, month: 1, day: 9 }, - { _id: '2019-1-10', year: 2019, month: 1, day: 10 }, - ]); - }); - }); - - it('should match sessions between 2019-1-11 and 2019-2-10', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 2, day: 10 }); - - expect($match).to.be.deep.equal({ - year: 2019, - $and: [ - { - $or: [{ month: { $gt: 1 } }, { month: 1, day: { $gte: 11 } }], - }, - { - $or: [{ month: { $lt: 2 } }, { month: 2, day: { $lte: 10 } }], - }, - ], - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.deep.equal(31); - expect(docs).to.be.deep.equal([ - { _id: '2019-1-11', year: 2019, month: 1, day: 11 }, - { _id: '2019-1-12', year: 2019, month: 1, day: 12 }, - { _id: '2019-1-13', year: 2019, month: 1, day: 13 }, - { _id: '2019-1-14', year: 2019, month: 1, day: 14 }, - { _id: '2019-1-15', year: 2019, month: 1, day: 15 }, - { _id: '2019-1-16', year: 2019, month: 1, day: 16 }, - { _id: '2019-1-17', year: 2019, month: 1, day: 17 }, - { _id: '2019-1-18', year: 2019, month: 1, day: 18 }, - { _id: '2019-1-19', year: 2019, month: 1, day: 19 }, - { _id: '2019-1-20', year: 2019, month: 1, day: 20 }, - { _id: '2019-1-21', year: 2019, month: 1, day: 21 }, - { _id: '2019-1-22', year: 2019, month: 1, day: 22 }, - { _id: '2019-1-23', year: 2019, month: 1, day: 23 }, - { _id: '2019-1-24', year: 2019, month: 1, day: 24 }, - { _id: '2019-1-25', year: 2019, month: 1, day: 25 }, - { _id: '2019-1-26', year: 2019, month: 1, day: 26 }, - { _id: '2019-1-27', year: 2019, month: 1, day: 27 }, - { _id: '2019-1-28', year: 2019, month: 1, day: 28 }, - { _id: '2019-1-29', year: 2019, month: 1, day: 29 }, - { _id: '2019-1-30', year: 2019, month: 1, day: 30 }, - { _id: '2019-1-31', year: 2019, month: 1, day: 31 }, - { _id: '2019-2-1', year: 2019, month: 2, day: 1 }, - { _id: '2019-2-2', year: 2019, month: 2, day: 2 }, - { _id: '2019-2-3', year: 2019, month: 2, day: 3 }, - { _id: '2019-2-4', year: 2019, month: 2, day: 4 }, - { _id: '2019-2-5', year: 2019, month: 2, day: 5 }, - { _id: '2019-2-6', year: 2019, month: 2, day: 6 }, - { _id: '2019-2-7', year: 2019, month: 2, day: 7 }, - { _id: '2019-2-8', year: 2019, month: 2, day: 8 }, - { _id: '2019-2-9', year: 2019, month: 2, day: 9 }, - { _id: '2019-2-10', year: 2019, month: 2, day: 10 }, - ]); - }); - }); - - it('should match sessions between 2019-5-1 and 2019-5-31', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 5, day: 31 }); - - expect($match).to.be.deep.equal({ - year: 2019, - month: 5, - day: { $gte: 1, $lte: 31 }, - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(31); - expect(docs).to.be.deep.equal([ - { _id: '2019-5-1', year: 2019, month: 5, day: 1 }, - { _id: '2019-5-2', year: 2019, month: 5, day: 2 }, - { _id: '2019-5-3', year: 2019, month: 5, day: 3 }, - { _id: '2019-5-4', year: 2019, month: 5, day: 4 }, - { _id: '2019-5-5', year: 2019, month: 5, day: 5 }, - { _id: '2019-5-6', year: 2019, month: 5, day: 6 }, - { _id: '2019-5-7', year: 2019, month: 5, day: 7 }, - { _id: '2019-5-8', year: 2019, month: 5, day: 8 }, - { _id: '2019-5-9', year: 2019, month: 5, day: 9 }, - { _id: '2019-5-10', year: 2019, month: 5, day: 10 }, - { _id: '2019-5-11', year: 2019, month: 5, day: 11 }, - { _id: '2019-5-12', year: 2019, month: 5, day: 12 }, - { _id: '2019-5-13', year: 2019, month: 5, day: 13 }, - { _id: '2019-5-14', year: 2019, month: 5, day: 14 }, - { _id: '2019-5-15', year: 2019, month: 5, day: 15 }, - { _id: '2019-5-16', year: 2019, month: 5, day: 16 }, - { _id: '2019-5-17', year: 2019, month: 5, day: 17 }, - { _id: '2019-5-18', year: 2019, month: 5, day: 18 }, - { _id: '2019-5-19', year: 2019, month: 5, day: 19 }, - { _id: '2019-5-20', year: 2019, month: 5, day: 20 }, - { _id: '2019-5-21', year: 2019, month: 5, day: 21 }, - { _id: '2019-5-22', year: 2019, month: 5, day: 22 }, - { _id: '2019-5-23', year: 2019, month: 5, day: 23 }, - { _id: '2019-5-24', year: 2019, month: 5, day: 24 }, - { _id: '2019-5-25', year: 2019, month: 5, day: 25 }, - { _id: '2019-5-26', year: 2019, month: 5, day: 26 }, - { _id: '2019-5-27', year: 2019, month: 5, day: 27 }, - { _id: '2019-5-28', year: 2019, month: 5, day: 28 }, - { _id: '2019-5-29', year: 2019, month: 5, day: 29 }, - { _id: '2019-5-30', year: 2019, month: 5, day: 30 }, - { _id: '2019-5-31', year: 2019, month: 5, day: 31 }, - ]); - }); - }); - - it('should match sessions between 2019-4-1 and 2019-4-30', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 4, day: 30 }); - - expect($match).to.be.deep.equal({ - year: 2019, - month: 4, - day: { $gte: 1, $lte: 30 }, - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(30); - expect(docs).to.be.deep.equal([ - { _id: '2019-4-1', year: 2019, month: 4, day: 1 }, - { _id: '2019-4-2', year: 2019, month: 4, day: 2 }, - { _id: '2019-4-3', year: 2019, month: 4, day: 3 }, - { _id: '2019-4-4', year: 2019, month: 4, day: 4 }, - { _id: '2019-4-5', year: 2019, month: 4, day: 5 }, - { _id: '2019-4-6', year: 2019, month: 4, day: 6 }, - { _id: '2019-4-7', year: 2019, month: 4, day: 7 }, - { _id: '2019-4-8', year: 2019, month: 4, day: 8 }, - { _id: '2019-4-9', year: 2019, month: 4, day: 9 }, - { _id: '2019-4-10', year: 2019, month: 4, day: 10 }, - { _id: '2019-4-11', year: 2019, month: 4, day: 11 }, - { _id: '2019-4-12', year: 2019, month: 4, day: 12 }, - { _id: '2019-4-13', year: 2019, month: 4, day: 13 }, - { _id: '2019-4-14', year: 2019, month: 4, day: 14 }, - { _id: '2019-4-15', year: 2019, month: 4, day: 15 }, - { _id: '2019-4-16', year: 2019, month: 4, day: 16 }, - { _id: '2019-4-17', year: 2019, month: 4, day: 17 }, - { _id: '2019-4-18', year: 2019, month: 4, day: 18 }, - { _id: '2019-4-19', year: 2019, month: 4, day: 19 }, - { _id: '2019-4-20', year: 2019, month: 4, day: 20 }, - { _id: '2019-4-21', year: 2019, month: 4, day: 21 }, - { _id: '2019-4-22', year: 2019, month: 4, day: 22 }, - { _id: '2019-4-23', year: 2019, month: 4, day: 23 }, - { _id: '2019-4-24', year: 2019, month: 4, day: 24 }, - { _id: '2019-4-25', year: 2019, month: 4, day: 25 }, - { _id: '2019-4-26', year: 2019, month: 4, day: 26 }, - { _id: '2019-4-27', year: 2019, month: 4, day: 27 }, - { _id: '2019-4-28', year: 2019, month: 4, day: 28 }, - { _id: '2019-4-29', year: 2019, month: 4, day: 29 }, - { _id: '2019-4-30', year: 2019, month: 4, day: 30 }, - ]); - }); - }); - - it('should match sessions between 2019-2-1 and 2019-2-28', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 2, day: 28 }); - - expect($match).to.be.deep.equal({ - year: 2019, - month: 2, - day: { $gte: 1, $lte: 28 }, - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(28); - expect(docs).to.be.deep.equal([ - { _id: '2019-2-1', year: 2019, month: 2, day: 1 }, - { _id: '2019-2-2', year: 2019, month: 2, day: 2 }, - { _id: '2019-2-3', year: 2019, month: 2, day: 3 }, - { _id: '2019-2-4', year: 2019, month: 2, day: 4 }, - { _id: '2019-2-5', year: 2019, month: 2, day: 5 }, - { _id: '2019-2-6', year: 2019, month: 2, day: 6 }, - { _id: '2019-2-7', year: 2019, month: 2, day: 7 }, - { _id: '2019-2-8', year: 2019, month: 2, day: 8 }, - { _id: '2019-2-9', year: 2019, month: 2, day: 9 }, - { _id: '2019-2-10', year: 2019, month: 2, day: 10 }, - { _id: '2019-2-11', year: 2019, month: 2, day: 11 }, - { _id: '2019-2-12', year: 2019, month: 2, day: 12 }, - { _id: '2019-2-13', year: 2019, month: 2, day: 13 }, - { _id: '2019-2-14', year: 2019, month: 2, day: 14 }, - { _id: '2019-2-15', year: 2019, month: 2, day: 15 }, - { _id: '2019-2-16', year: 2019, month: 2, day: 16 }, - { _id: '2019-2-17', year: 2019, month: 2, day: 17 }, - { _id: '2019-2-18', year: 2019, month: 2, day: 18 }, - { _id: '2019-2-19', year: 2019, month: 2, day: 19 }, - { _id: '2019-2-20', year: 2019, month: 2, day: 20 }, - { _id: '2019-2-21', year: 2019, month: 2, day: 21 }, - { _id: '2019-2-22', year: 2019, month: 2, day: 22 }, - { _id: '2019-2-23', year: 2019, month: 2, day: 23 }, - { _id: '2019-2-24', year: 2019, month: 2, day: 24 }, - { _id: '2019-2-25', year: 2019, month: 2, day: 25 }, - { _id: '2019-2-26', year: 2019, month: 2, day: 26 }, - { _id: '2019-2-27', year: 2019, month: 2, day: 27 }, - { _id: '2019-2-28', year: 2019, month: 2, day: 28 }, - ]); - }); - }); - - it('should match sessions between 2019-1-28 and 2019-2-27', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ year: 2019, month: 2, day: 27 }); - - expect($match).to.be.deep.equal({ - year: 2019, - $and: [ - { - $or: [{ month: { $gt: 1 } }, { month: 1, day: { $gte: 28 } }], - }, - { - $or: [{ month: { $lt: 2 } }, { month: 2, day: { $lte: 27 } }], - }, - ], - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(31); - expect(docs).to.be.deep.equal([ - { _id: '2019-1-28', year: 2019, month: 1, day: 28 }, - { _id: '2019-1-29', year: 2019, month: 1, day: 29 }, - { _id: '2019-1-30', year: 2019, month: 1, day: 30 }, - { _id: '2019-1-31', year: 2019, month: 1, day: 31 }, - { _id: '2019-2-1', year: 2019, month: 2, day: 1 }, - { _id: '2019-2-2', year: 2019, month: 2, day: 2 }, - { _id: '2019-2-3', year: 2019, month: 2, day: 3 }, - { _id: '2019-2-4', year: 2019, month: 2, day: 4 }, - { _id: '2019-2-5', year: 2019, month: 2, day: 5 }, - { _id: '2019-2-6', year: 2019, month: 2, day: 6 }, - { _id: '2019-2-7', year: 2019, month: 2, day: 7 }, - { _id: '2019-2-8', year: 2019, month: 2, day: 8 }, - { _id: '2019-2-9', year: 2019, month: 2, day: 9 }, - { _id: '2019-2-10', year: 2019, month: 2, day: 10 }, - { _id: '2019-2-11', year: 2019, month: 2, day: 11 }, - { _id: '2019-2-12', year: 2019, month: 2, day: 12 }, - { _id: '2019-2-13', year: 2019, month: 2, day: 13 }, - { _id: '2019-2-14', year: 2019, month: 2, day: 14 }, - { _id: '2019-2-15', year: 2019, month: 2, day: 15 }, - { _id: '2019-2-16', year: 2019, month: 2, day: 16 }, - { _id: '2019-2-17', year: 2019, month: 2, day: 17 }, - { _id: '2019-2-18', year: 2019, month: 2, day: 18 }, - { _id: '2019-2-19', year: 2019, month: 2, day: 19 }, - { _id: '2019-2-20', year: 2019, month: 2, day: 20 }, - { _id: '2019-2-21', year: 2019, month: 2, day: 21 }, - { _id: '2019-2-22', year: 2019, month: 2, day: 22 }, - { _id: '2019-2-23', year: 2019, month: 2, day: 23 }, - { _id: '2019-2-24', year: 2019, month: 2, day: 24 }, - { _id: '2019-2-25', year: 2019, month: 2, day: 25 }, - { _id: '2019-2-26', year: 2019, month: 2, day: 26 }, - { _id: '2019-2-27', year: 2019, month: 2, day: 27 }, - ]); - }); - }); - - it('should have sessions data saved', () => { - const collection = db.collection('sessions'); - return collection - .find() - .toArray() - .then((docs) => expect(docs.length).to.be.equal(DATA.sessions.length)); - }); - - it('should generate daily sessions', () => { - const collection = db.collection('sessions'); - return aggregates - .dailySessionsOfYesterday(collection, { year: 2019, month: 5, day: 2 }) - .toArray() - .then(async (docs) => { - docs.forEach((doc) => { - doc._id = `${doc.userId}-${doc.year}-${doc.month}-${doc.day}`; - }); - - await collection.insertMany(docs); - - expect(docs.length).to.be.equal(3); - expect(docs).to.be.deep.equal([ - { - _id: 'xPZXw9xqM3kKshsse-2019-5-2', - time: 5814, - sessions: 3, - devices: [ - { - sessions: 2, - time: 5528, - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - }, - { - sessions: 1, - time: 286, - device: { - type: 'browser', - name: 'Firefox', - longVersion: '66.0.3', - os: { - name: 'Linux', - version: '12', - }, - version: '66.0.3', - }, - }, - ], - type: 'user_daily', - _computedAt: docs[0]._computedAt, - day: 2, - month: 5, - year: 2019, - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - }, - { - _id: 'xPZXw9xqM3kKshsse-2019-4-30', - day: 30, - devices: [ - { - device: { - longVersion: '66.0.3', - name: 'Firefox', - os: { - name: 'Linux', - version: '12', - }, - type: 'browser', - version: '66.0.3', - }, - sessions: 1, - time: 286, - }, - ], - month: 4, - sessions: 1, - time: 286, - type: 'user_daily', - _computedAt: docs[1]._computedAt, - userId: 'xPZXw9xqM3kKshsse', - mostImportantRole: 'user', - year: 2019, - }, - { - _id: 'xPZXw9xqM3kKshsse2-2019-5-1', - time: 4167, - sessions: 1, - devices: [ - { - sessions: 1, - time: 4167, - device: { - type: 'browser', - name: 'Chrome', - longVersion: '73.0.3683.103', - os: { - name: 'Mac OS', - version: '10.14.1', - }, - version: '73.0.3683', - }, - }, - ], - type: 'user_daily', - _computedAt: docs[2]._computedAt, - day: 1, - month: 5, - year: 2019, - userId: 'xPZXw9xqM3kKshsse2', - mostImportantRole: 'admin', - }, - ]); - }); - }); - - it('should have 2 unique users for month 5 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueUsersOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31 }).then((docs) => { - expect(docs.length).to.be.equal(1); - expect(docs).to.be.deep.equal([ - { - count: 2, - roles: [ - { - count: 1, - role: 'user', - sessions: 3, - time: 5814, - }, - { - count: 1, - role: 'admin', - sessions: 1, - time: 4167, - }, - ], - sessions: 4, - time: 9981, - }, - ]); - }); - }); - - it('should have 1 unique user for 1st of month 5 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueUsersOfYesterday(collection, { year: 2019, month: 5, day: 1 }).then((docs) => { - expect(docs.length).to.be.equal(1); - expect(docs).to.be.deep.equal([ - { - count: 1, - roles: [ - { - count: 1, - role: 'admin', - sessions: 1, - time: 4167, - }, - ], - sessions: 1, - time: 4167, - }, - ]); - }); - }); - - it('should have 1 unique user for 2nd of month 5 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueUsersOfYesterday(collection, { year: 2019, month: 5, day: 2 }).then((docs) => { - expect(docs.length).to.be.equal(1); - expect(docs).to.be.deep.equal([ - { - count: 1, - roles: [ - { - count: 1, - role: 'user', - sessions: 3, - time: 5814, - }, - ], - sessions: 3, - time: 5814, - }, - ]); - }); - }); - - it('should have 2 unique devices for month 5 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueDevicesOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31 }).then((docs) => { - expect(docs.length).to.be.equal(2); - expect(docs).to.be.deep.equal([ - { - count: 3, - time: 9695, - type: 'browser', - name: 'Chrome', - version: '73.0.3683', - }, - { - count: 1, - time: 286, - type: 'browser', - name: 'Firefox', - version: '66.0.3', - }, - ]); - }); - }); - - it('should have 2 unique devices for 2nd of month 5 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueDevicesOfYesterday(collection, { year: 2019, month: 5, day: 2 }).then((docs) => { - expect(docs.length).to.be.equal(2); - expect(docs).to.be.deep.equal([ - { - count: 2, - time: 5528, - type: 'browser', - name: 'Chrome', - version: '73.0.3683', - }, - { - count: 1, - time: 286, - type: 'browser', - name: 'Firefox', - version: '66.0.3', - }, - ]); - }); - }); - - it('should have 2 unique OS for month 5 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueOSOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31 }).then((docs) => { - expect(docs.length).to.be.equal(2); - expect(docs).to.be.deep.equal([ - { - count: 3, - time: 9695, - name: 'Mac OS', - version: '10.14.1', - }, - { - count: 1, - time: 286, - name: 'Linux', - version: '12', - }, - ]); - }); - }); - - it('should have 2 unique OS for 2nd of month 5 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueOSOfYesterday(collection, { year: 2019, month: 5, day: 2 }).then((docs) => { - expect(docs.length).to.be.equal(2); - expect(docs).to.be.deep.equal([ - { - count: 2, - time: 5528, - name: 'Mac OS', - version: '10.14.1', - }, - { - count: 1, - time: 286, - name: 'Linux', - version: '12', - }, - ]); - }); - }); - - it('should match sessions between 2018-12-29 and 2019-1-4', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ - year: 2019, - month: 1, - day: 4, - type: 'week', - }); - - expect($match).to.be.deep.equal({ - $and: [ - { - $or: [{ year: { $gt: 2018 } }, { year: 2018, month: { $gt: 12 } }, { year: 2018, month: 12, day: { $gte: 29 } }], - }, - { - $or: [{ year: { $lt: 2019 } }, { year: 2019, month: { $lt: 1 } }, { year: 2019, month: 1, day: { $lte: 4 } }], - }, - ], - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(7); - expect(docs).to.be.deep.equal([ - { _id: '2018-12-29', year: 2018, month: 12, day: 29 }, - { _id: '2018-12-30', year: 2018, month: 12, day: 30 }, - { _id: '2018-12-31', year: 2018, month: 12, day: 31 }, - { _id: '2019-1-1', year: 2019, month: 1, day: 1 }, - { _id: '2019-1-2', year: 2019, month: 1, day: 2 }, - { _id: '2019-1-3', year: 2019, month: 1, day: 3 }, - { _id: '2019-1-4', year: 2019, month: 1, day: 4 }, - ]); - }); - }); - - it('should match sessions between 2019-1-29 and 2019-2-4', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ - year: 2019, - month: 2, - day: 4, - type: 'week', - }); - - expect($match).to.be.deep.equal({ - year: 2019, - $and: [ - { - $or: [{ month: { $gt: 1 } }, { month: 1, day: { $gte: 29 } }], - }, - { - $or: [{ month: { $lt: 2 } }, { month: 2, day: { $lte: 4 } }], - }, - ], - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(7); - expect(docs).to.be.deep.equal([ - { _id: '2019-1-29', year: 2019, month: 1, day: 29 }, - { _id: '2019-1-30', year: 2019, month: 1, day: 30 }, - { _id: '2019-1-31', year: 2019, month: 1, day: 31 }, - { _id: '2019-2-1', year: 2019, month: 2, day: 1 }, - { _id: '2019-2-2', year: 2019, month: 2, day: 2 }, - { _id: '2019-2-3', year: 2019, month: 2, day: 3 }, - { _id: '2019-2-4', year: 2019, month: 2, day: 4 }, - ]); - }); - }); - - it('should match sessions between 2019-5-1 and 2019-5-7', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ - year: 2019, - month: 5, - day: 7, - type: 'week', - }); - - expect($match).to.be.deep.equal({ - year: 2019, - month: 5, - day: { $gte: 1, $lte: 7 }, - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(7); - expect(docs).to.be.deep.equal([ - { _id: '2019-5-1', year: 2019, month: 5, day: 1 }, - { _id: '2019-5-2', year: 2019, month: 5, day: 2 }, - { _id: '2019-5-3', year: 2019, month: 5, day: 3 }, - { _id: '2019-5-4', year: 2019, month: 5, day: 4 }, - { _id: '2019-5-5', year: 2019, month: 5, day: 5 }, - { _id: '2019-5-6', year: 2019, month: 5, day: 6 }, - { _id: '2019-5-7', year: 2019, month: 5, day: 7 }, - ]); - }); - }); - - it('should match sessions between 2019-5-7 and 2019-5-14', () => { - const collection = db.collection('sessions_dates'); - const $match = aggregates.getMatchOfLastMonthOrWeek({ - year: 2019, - month: 5, - day: 14, - type: 'week', - }); - - expect($match).to.be.deep.equal({ - year: 2019, - month: 5, - day: { $gte: 8, $lte: 14 }, - }); - - return collection - .aggregate([ - { - $match, - }, - ]) - .toArray() - .then((docs) => { - expect(docs.length).to.be.equal(7); - expect(docs).to.be.deep.equal([ - { _id: '2019-5-8', year: 2019, month: 5, day: 8 }, - { _id: '2019-5-9', year: 2019, month: 5, day: 9 }, - { _id: '2019-5-10', year: 2019, month: 5, day: 10 }, - { _id: '2019-5-11', year: 2019, month: 5, day: 11 }, - { _id: '2019-5-12', year: 2019, month: 5, day: 12 }, - { _id: '2019-5-13', year: 2019, month: 5, day: 13 }, - { _id: '2019-5-14', year: 2019, month: 5, day: 14 }, - ]); - }); - }); - - it('should have 0 unique users for the week ending on 5/31 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueUsersOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 31, type: 'week' }).then((docs) => { - expect(docs.length).to.be.equal(0); - }); - }); - - it('should have 2 unique users for the week ending on 5/7 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueUsersOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 7, type: 'week' }).then((docs) => { - expect(docs.length).to.be.equal(1); - expect(docs).to.be.deep.equal([ - { - count: 2, - roles: [ - { - count: 1, - role: 'user', - sessions: 3, - time: 5814, - }, - { - count: 1, - role: 'admin', - sessions: 1, - time: 4167, - }, - ], - sessions: 4, - time: 9981, - }, - ]); - }); - }); - - it('should have 2 unique devices for the week ending on 5/7 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueDevicesOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 7, type: 'week' }).then((docs) => { - expect(docs.length).to.be.equal(2); - expect(docs).to.be.deep.equal([ - { - count: 3, - time: 9695, - type: 'browser', - name: 'Chrome', - version: '73.0.3683', - }, - { - count: 1, - time: 286, - type: 'browser', - name: 'Firefox', - version: '66.0.3', - }, - ]); - }); - }); - - it('should have 2 unique OS for the week ending on 5/7 of 2019', () => { - const collection = db.collection('sessions'); - return aggregates.getUniqueOSOfLastMonthOrWeek(collection, { year: 2019, month: 5, day: 7 }).then((docs) => { - expect(docs.length).to.be.equal(2); - expect(docs).to.be.deep.equal([ - { - count: 3, - time: 9695, - name: 'Mac OS', - version: '10.14.1', - }, - { - count: 2, - time: 572, - name: 'Linux', - version: '12', - }, - ]); - }); - }); -}); From 0c98a2acd61c9cdb66e6ab597c4bdc216f6b0bbd Mon Sep 17 00:00:00 2001 From: Pedro Berleze Rorato <41977327+PedroRorato@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:58:44 -0300 Subject: [PATCH 086/149] chore: Update VideoConferenceBlock user stack size (#29647) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../src/blocks/VideoConferenceBlock/VideoConferenceBlock.tsx | 2 +- .../src/VideoConfMessage/VideoConfMessageUserStack.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fuselage-ui-kit/src/blocks/VideoConferenceBlock/VideoConferenceBlock.tsx b/packages/fuselage-ui-kit/src/blocks/VideoConferenceBlock/VideoConferenceBlock.tsx index 8dfc0449c987..e595d2706e7b 100644 --- a/packages/fuselage-ui-kit/src/blocks/VideoConferenceBlock/VideoConferenceBlock.tsx +++ b/packages/fuselage-ui-kit/src/blocks/VideoConferenceBlock/VideoConferenceBlock.tsx @@ -24,7 +24,7 @@ import { kitContext } from '../..'; type VideoConferenceBlockProps = BlockProps<UiKit.VideoConferenceBlock>; -const MAX_USERS = 6; +const MAX_USERS = 3; const VideoConferenceBlock = ({ block, diff --git a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageUserStack.tsx b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageUserStack.tsx index 2ca03a814040..56f5e0a9fa13 100644 --- a/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageUserStack.tsx +++ b/packages/ui-video-conf/src/VideoConfMessage/VideoConfMessageUserStack.tsx @@ -3,7 +3,7 @@ import { Avatar, Box } from '@rocket.chat/fuselage'; import { useUserAvatarPath } from '@rocket.chat/ui-contexts'; import { ReactElement, memo } from 'react'; -const MAX_USERS = 6; +const MAX_USERS = 3; const VideoConfMessageUserStack = ({ users }: { users: Serialized<IVideoConferenceUser>[] }): ReactElement => { const getUserAvatarPath = useUserAvatarPath(); From 3b09028b2c885af240c40eaaebf7957f2f7929bc Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Thu, 6 Jul 2023 19:08:49 -0300 Subject: [PATCH 087/149] regression: Wrong addon usage on account menu itens (#29754) --- apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx index 6265a380fe67..5e6f4c7d183a 100644 --- a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx @@ -32,7 +32,7 @@ export const useAccountItems = (): GenericMenuItemProps[] => { content: t('Feature_preview'), onClick: handleFeaturePreview, ...(unseenFeatures > 0 && { - addon: () => ( + addon: ( <Badge variant='primary' aria-label={t('Unseen_features')}> {unseenFeatures} </Badge> From 2ae66ab53a918177ed60d83ef89b4eea25ffb8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Thu, 6 Jul 2023 19:18:33 -0300 Subject: [PATCH 088/149] regression: `UserMenu` add missing translations (#29749) --- apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx b/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx index 4458deb26f42..232011dbe315 100644 --- a/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx @@ -1,4 +1,5 @@ import type { IUser } from '@rocket.chat/core-typings'; +import { useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import UserMenuHeader from '../UserMenuHeader'; @@ -7,6 +8,8 @@ import { useStatusItems } from './useStatusItems'; import { useThemeItems } from './useThemeItems'; export const useUserMenu = (user: IUser) => { + const t = useTranslation(); + const statusItems = useStatusItems(user); const themeItems = useThemeItems(); const accountItems = useAccountItems(); @@ -17,11 +20,11 @@ export const useUserMenu = (user: IUser) => { items: [], }, { - title: 'Status', + title: t('Status'), items: statusItems, }, { - title: 'Theme', + title: t('Theme'), items: themeItems, }, { From 0f2f37d4db67b87779fa06fe6351d7b4e510ecb7 Mon Sep 17 00:00:00 2001 From: Diego Sampaio <chinello@gmail.com> Date: Thu, 6 Jul 2023 23:24:05 -0300 Subject: [PATCH 089/149] ci: Improve release automation (#29752) --- .changeset/config.json | 2 +- .changeset/nine-yaks-draw.md | 5 ++ .github/workflows/changesets.yml | 51 ------------------- .github/workflows/publish-release.yml | 3 +- package.json | 1 - .../release-action/src/bumpNextVersion.ts | 12 ++--- .../src/fixWorkspaceVersionsBeforePublish.ts | 4 -- packages/release-action/src/gitUtils.ts | 38 +++++++++++++- packages/release-action/src/publishRelease.ts | 17 +++++-- .../release-action/src/startPatchRelease.ts | 2 +- yarn.lock | 33 +----------- 11 files changed, 66 insertions(+), 102 deletions(-) create mode 100644 .changeset/nine-yaks-draw.md delete mode 100644 .github/workflows/changesets.yml diff --git a/.changeset/config.json b/.changeset/config.json index 5dfd3f70619b..9d77693b3b31 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,6 +1,6 @@ { "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", - "changelog": ["@changesets/changelog-github", { "repo": "RocketChat/Rocket.Chat" }], + "changelog": "@changesets/changelog-git", "commit": false, "fixed": [ ["@rocket.chat/meteor", "@rocket.chat/core-typings", "@rocket.chat/rest-typings"] diff --git a/.changeset/nine-yaks-draw.md b/.changeset/nine-yaks-draw.md new file mode 100644 index 000000000000..bd12a98350fb --- /dev/null +++ b/.changeset/nine-yaks-draw.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/release-action': minor +--- + +Use `release-automation` branch to perform the release diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml deleted file mode 100644 index 56759167493f..000000000000 --- a/.github/workflows/changesets.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Changesets - -on: - push: - branches: - - develop - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - release-versions: - name: ⚙️ Variables Setup - runs-on: ubuntu-latest - outputs: - node-version: ${{ steps.var.outputs.node-version }} - steps: - - uses: Bhacaz/checkout-files@v2 - with: - files: package.json - branch: ${{ github.ref }} - - - id: var - run: | - NODE_VERSION=$(node -p "require('./package.json').engines.node") - echo "NODE_VERSION: ${NODE_VERSION}" - echo "node-version=${NODE_VERSION}" >> $GITHUB_OUTPUT - - release: - name: Release - needs: [release-versions] - runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: actions/checkout@v3 - - - name: Setup NodeJS - uses: ./.github/actions/setup-node - with: - node-version: ${{ needs.release-versions.outputs.node-version }} - cache-modules: true - install: true - - - uses: dtinth/setup-github-actions-caching-for-turbo@v1 - - - name: Create Release Pull Request - uses: changesets/action@v1 - with: - title: 'chore: Bump packages' - env: - HUSKY: 0 - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index cb85da9fad6c..ba66adc5d29e 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -3,7 +3,7 @@ name: Publish Final Release on: push: branches: - - master + - release-automation concurrency: ${{ github.workflow }}-${{ github.ref }} @@ -18,6 +18,7 @@ jobs: - name: Checkout Repo uses: actions/checkout@v3 with: + fetch-depth: 0 token: ${{ secrets.CI_PAT }} - name: Setup NodeJS diff --git a/package.json b/package.json index 7a79038a3acf..a47523eefcea 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "fuselage": "./fuselage.sh" }, "devDependencies": { - "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.26.1", "@types/chart.js": "^2.9.37", "@types/js-yaml": "^4.0.5", diff --git a/packages/release-action/src/bumpNextVersion.ts b/packages/release-action/src/bumpNextVersion.ts index cc977900b213..999fa3d7b49b 100644 --- a/packages/release-action/src/bumpNextVersion.ts +++ b/packages/release-action/src/bumpNextVersion.ts @@ -9,6 +9,7 @@ import { setupOctokit } from './setupOctokit'; import { createNpmFile } from './createNpmFile'; import { getChangelogEntry, bumpFileVersions, readPackageJson } from './utils'; import { fixWorkspaceVersionsBeforePublish } from './fixWorkspaceVersionsBeforePublish'; +import { commitChanges, createBranch, createTag, pushNewBranch } from './gitUtils'; export async function bumpNextVersion({ githubToken, @@ -59,26 +60,25 @@ export async function bumpNextVersion({ await bumpFileVersions(cwd, currentVersion, newVersion); // TODO check if branch exists - await exec('git', ['checkout', '-b', newBranch]); + await createBranch(newBranch); - await exec('git', ['add', '.']); - await exec('git', ['commit', '-m', newVersion]); + await commitChanges(`Release ${newVersion}`); core.info('fix dependencies in workspace packages'); await fixWorkspaceVersionsBeforePublish(); await exec('yarn', ['changeset', 'publish', '--no-git-tag']); - await exec('git', ['tag', newVersion]); + await createTag(newVersion); - await exec('git', ['push', '--force', '--follow-tags', 'origin', `HEAD:refs/heads/${newBranch}`]); + await pushNewBranch(newBranch); if (newVersion.includes('rc.0')) { const finalPrTitle = `Release ${finalVersion}`; core.info('creating pull request'); await octokit.rest.pulls.create({ - base: 'master', + base: 'release-automation', head: newBranch, title: finalPrTitle, body: prBody, diff --git a/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts b/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts index 1a5780996fef..3a45e4fb8794 100644 --- a/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts +++ b/packages/release-action/src/fixWorkspaceVersionsBeforePublish.ts @@ -41,10 +41,6 @@ export async function fixWorkspaceVersionsBeforePublish() { for (const dependency of dependencies) { const dependencyVersion = packageJson[dependencyType][dependency]; if (dependencyVersion.startsWith('workspace:')) { - if (!dependencyVersion.startsWith('workspace:^')) { - throw new Error(`Unsupported workspace version range: ${dependencyVersion}`); - } - const realVersion = workspaceVersions.get(dependency); if (!realVersion) { throw new Error(`Could not find version for workspace ${dependency}`); diff --git a/packages/release-action/src/gitUtils.ts b/packages/release-action/src/gitUtils.ts index 0550841f2810..808f6d8885fa 100644 --- a/packages/release-action/src/gitUtils.ts +++ b/packages/release-action/src/gitUtils.ts @@ -1,6 +1,42 @@ -import { exec } from '@actions/exec'; +import { exec, getExecOutput } from '@actions/exec'; export async function setupGitUser() { await exec('git', ['config', 'user.name', '"rocketchat-github-ci"']); await exec('git', ['config', 'user.email', '"buildmaster@rocket.chat"']); } + +export async function createBranch(newBranch: string) { + await exec('git', ['checkout', '-b', newBranch]); +} + +export async function checkoutBranch(branchName: string) { + await exec('git', ['checkout', branchName]); +} + +export async function mergeBranch(branchName: string) { + await exec('git', ['merge', '--no-edit', branchName]); +} + +export async function commitChanges(commitMessage: string) { + await exec('git', ['add', '.']); + await exec('git', ['commit', '-m', commitMessage]); +} + +export async function createTag(version: string) { + // create an annotated tag so git push --follow-tags will push the tag + await exec('git', ['tag', version, '-m', version]); +} + +export async function getCurrentBranch() { + const { stdout: branchName } = await getExecOutput('git', ['rev-parse', '--abbrev-ref', 'HEAD']); + + return branchName.trim(); +} + +export async function pushChanges() { + await exec('git', ['push', '--follow-tags']); +} + +export async function pushNewBranch(newBranch: string) { + await exec('git', ['push', '--force', '--follow-tags', 'origin', `HEAD:refs/heads/${newBranch}`]); +} diff --git a/packages/release-action/src/publishRelease.ts b/packages/release-action/src/publishRelease.ts index a776c9d74d26..e9d76d19a75e 100644 --- a/packages/release-action/src/publishRelease.ts +++ b/packages/release-action/src/publishRelease.ts @@ -9,6 +9,7 @@ import { createNpmFile } from './createNpmFile'; import { setupOctokit } from './setupOctokit'; import { bumpFileVersions, getChangelogEntry, readPackageJson } from './utils'; import { fixWorkspaceVersionsBeforePublish } from './fixWorkspaceVersionsBeforePublish'; +import { checkoutBranch, commitChanges, createTag, getCurrentBranch, mergeBranch, pushChanges } from './gitUtils'; export async function publishRelease({ githubToken, @@ -29,7 +30,7 @@ export async function publishRelease({ await createNpmFile(); if (baseRef) { - await exec('git', ['checkout', baseRef]); + await checkoutBranch(baseRef); } const { version: currentVersion } = await readPackageJson(cwd); @@ -73,17 +74,23 @@ export async function publishRelease({ core.info('update version in all files to new'); await bumpFileVersions(cwd, currentVersion, newVersion); - await exec('git', ['add', '.']); - await exec('git', ['commit', '-m', `Release ${newVersion}`]); + await commitChanges(`Release ${newVersion}`); + + // get current branch name + const branchName = await getCurrentBranch(); + + // merge release changes to master + await checkoutBranch('master'); + await mergeBranch(branchName); core.info('fix dependencies in workspace packages'); await fixWorkspaceVersionsBeforePublish(); await exec('yarn', ['changeset', 'publish', '--no-git-tag']); - await exec('git', ['tag', newVersion]); + await createTag(newVersion); - await exec('git', ['push', '--follow-tags']); + await pushChanges(); core.info('create release'); await octokit.rest.repos.createRelease({ diff --git a/packages/release-action/src/startPatchRelease.ts b/packages/release-action/src/startPatchRelease.ts index 05681604e47c..a499b223608c 100644 --- a/packages/release-action/src/startPatchRelease.ts +++ b/packages/release-action/src/startPatchRelease.ts @@ -48,7 +48,7 @@ export async function startPatchRelease({ core.info('creating pull request'); await octokit.rest.pulls.create({ - base: 'master', + base: 'release-automation', head: newBranch, title: finalPrTitle, body: '', diff --git a/yarn.lock b/yarn.lock index 7bc670d2dd2a..df384064d532 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4165,17 +4165,6 @@ __metadata: languageName: node linkType: hard -"@changesets/changelog-github@npm:^0.4.8": - version: 0.4.8 - resolution: "@changesets/changelog-github@npm:0.4.8" - dependencies: - "@changesets/get-github-info": ^0.5.2 - "@changesets/types": ^5.2.1 - dotenv: ^8.1.0 - checksum: 8a357cc08757e0eeca267ee05141f68bef936582abef8b78a5d30d99f5a86e41b7d3debba70992b73b2f57b0fc6201ec1cc3c65116930167ee3197b427b865c5 - languageName: node - linkType: hard - "@changesets/cli@npm:^2.26.1": version: 2.26.1 resolution: "@changesets/cli@npm:2.26.1" @@ -4256,16 +4245,6 @@ __metadata: languageName: node linkType: hard -"@changesets/get-github-info@npm:^0.5.2": - version: 0.5.2 - resolution: "@changesets/get-github-info@npm:0.5.2" - dependencies: - dataloader: ^1.4.0 - node-fetch: ^2.5.0 - checksum: 067e07eeaecdbedbd1c715513c4aa6206a941bd1d3af292d067792808c6fa6644caad2b35fba614a44892559c031c234df8028f8d2abd4cb2682d48080ef5df3 - languageName: node - linkType: hard - "@changesets/get-release-plan@npm:^3.0.16": version: 3.0.16 resolution: "@changesets/get-release-plan@npm:3.0.16" @@ -20149,13 +20128,6 @@ __metadata: languageName: node linkType: hard -"dataloader@npm:^1.4.0": - version: 1.4.0 - resolution: "dataloader@npm:1.4.0" - checksum: e2c93d43afde68980efc0cd9ff48e9851116e27a9687f863e02b56d46f7e7868cc762cd6dcbaf4197e1ca850a03651510c165c2ae24b8e9843fd894002ad0e20 - languageName: node - linkType: hard - "date-fns@npm:^2.15.0, date-fns@npm:^2.28.0": version: 2.28.0 resolution: "date-fns@npm:2.28.0" @@ -21058,7 +21030,7 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^8.0.0, dotenv@npm:^8.1.0": +"dotenv@npm:^8.0.0": version: 8.6.0 resolution: "dotenv@npm:8.6.0" checksum: 38e902c80b0666ab59e9310a3d24ed237029a7ce34d976796349765ac96b8d769f6df19090f1f471b77a25ca391971efde8a1ea63bb83111bd8bec8e5cc9b2cd @@ -30812,7 +30784,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.5.0, node-fetch@npm:^2.6.11": +"node-fetch@npm:^2.6.11": version: 2.6.11 resolution: "node-fetch@npm:2.6.11" dependencies: @@ -35958,7 +35930,6 @@ __metadata: version: 0.0.0-use.local resolution: "rocket.chat@workspace:." dependencies: - "@changesets/changelog-github": ^0.4.8 "@changesets/cli": ^2.26.1 "@types/chart.js": ^2.9.37 "@types/js-yaml": ^4.0.5 From e72e953c90fccffe897b3f539b4ef219cc55f2ff Mon Sep 17 00:00:00 2001 From: AdityaSingh-02 <asaditya2002@gmail.com> Date: Fri, 7 Jul 2023 10:13:46 +0530 Subject: [PATCH 090/149] Refactor: Modified Links --- .tours/2---how-a-message-is-sent.tour | 14 +- .tours/3---how-a-message-is-sent.tour | 150 +++++++++++----------- .tours/4---how-to-create-an-endpoint.tour | 22 ++-- 3 files changed, 93 insertions(+), 93 deletions(-) diff --git a/.tours/2---how-a-message-is-sent.tour b/.tours/2---how-a-message-is-sent.tour index bae6fcc125c7..8509b85a7d43 100644 --- a/.tours/2---how-a-message-is-sent.tour +++ b/.tours/2---how-a-message-is-sent.tour @@ -15,7 +15,7 @@ { "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", "description": "### Chat/Room Layout\n\n- The \"RoomBody.tsx\" file is straightforward to comprehend. You can easily navigate through the components and grasp their functionalities.\n\n- The majority of the code in this file revolves around message rendering and handling various chat-related actions.", - "line": 539, + "line": 543, "selection": { "start": { "line": 464, @@ -30,7 +30,7 @@ { "file": "apps/meteor/client/views/room/components/body/RoomBody.tsx", "description": "### Composer Container\n\n- At the bottom of the \"RoomBody.tsx\" file, there is a component responsible for rendering the Message Composer. It accepts several props, including the room ID and subscription details. These props help verify whether the user is allowed to send messages in the given context.\n\n- The Message Composer component also manages the size and layout of the composer, allowing users to comfortably compose and send messages. It provides functionality to retrieve the previous and next messages for reference or navigation purposes.\n\n```\n <ComposerContainer />\n```\n\n- **Note** ComposerContainer is also built from combination multiple components, we will explore components and see how data is sent.\n", - "line": 630 + "line": 635 }, { "file": "apps/meteor/client/views/room/components/body/composer/ComposerContainer.tsx", @@ -45,7 +45,7 @@ { "file": "apps/meteor/client/views/room/components/body/composer/ComposerMessage.tsx", "description": "## MessageBox Component\n\n- At the bottom of the code, there is a \"MessageBox\" component, which represents the actual chat message box. Its presence is crucial for the proper functioning of the Composer. If you were to comment out or remove the \"MessageBox\" component and run the server, you would observe that the Composer functionality would no longer be available.\n\n- The \"MessageBox\" component plays a pivotal role in enabling users to compose and send messages within the chat interface. It provides the necessary user interface elements, such as input fields and buttons, to facilitate message composition and submission.", - "line": 80 + "line": 81 }, { "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", @@ -55,7 +55,7 @@ { "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", "description": "## Sending Message\n\n#### Rocket chat's Room/channel message composers have multiple options such as uploading files, Writing text messages and Quoted messages, And then we have a send button at the right corner of composer which sends what you have entered.\n\n#### To understand how the send button works for sending a simple text message:\n\n- When the send button is clicked, an event is triggered.\n- The event handler associated with the send button retrieves the text message entered by the user.\n- The text message is then processed and prepared for sending.\n- The necessary information, such as the room/channel ID, sender details, and the text message content, is included.\n- The prepared message is sent to the server via an appropriate network request or function call.\n- The server receives the message and performs further processing, including broadcasting it to the relevant recipients in the room/channel.\nThe message is displayed in the chat interface for all participants to view.", - "line": 345, + "line": 347, "selection": { "start": { "line": 3, @@ -70,16 +70,16 @@ { "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", "description": "## Message Input \n\n- The MessageComposerInput component plays a crucial role in handling user inputs within the message composer. It is responsible for capturing various types of inputs and encompasses multiple associated actions.\n\n#### The MessageComposerInput component enables users to interact with the message composer through different actions, such as:\n\n1. **Typing and Editing**: Users can enter and edit text within the composer input field.\n\n2. **Formatting**: It may support various formatting options like bold, italic, bullet points, etc., allowing users to apply formatting to their messages.\n\n3. **Mentions**: Users can mention specific individuals or groups within the message by using the appropriate syntax or by selecting them from a list.\n\n4. **Emojis**: It may provide an emoji picker or support emoji shorthand, allowing users to insert emojis into their messages.\n\n5. **Attachments**: Users can attach files or media to their messages, such as images, documents, or videos.", - "line": 380 + "line": 382 }, { "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", "description": "### The Send Button\n\n- To send any message you need to click send button or hit Enter/Return key, when you click send button **handleSendMessage** method/function is called.\n```\n <MessageComposerAction\n onClick={handleSendMessage}\n ....\n />\n```", - "line": 428 + "line": 430 }, { "file": "apps/meteor/client/views/room/components/body/composer/messageBox/MessageBox.tsx", - "description": "## handleSendMessage Function\n\n- This is the function which sends messages further, it Gets text from chat.composer.text where chat is actully using useChat() which is coming from [ChatAPI](./apps/meteor/client/lib/chats/ChatAPI.ts) consisting of multiple functions.\n\n#### The handleSendMessage function is responsible for processing and sending the message to the intended recipients. It performs the following actions:\n\n1. Retrieves the content of the message entered by the user.\n2. Collects additional information needed for sending the message, such as the room/channel ID, sender details, and any associated metadata.\n3. Packages the message data into a suitable format for transmission.\n4. Initiates the process of sending the message, it goes through a WebSocket connection.\n5. The message is then transmitted to the server for further processing.\n6. The server handles the message, ensuring it is delivered to the specified room/channel and received by the intended recipients.\n7. Once the message is successfully sent, it may trigger updates to the chat interface, including displaying the sent message in the conversation history.\n\n- Here we have \n ```\n const chat = useChat(); // useChat is a Context using ChatAPI.\n const text = chat.composer?.text ?? ''; //Text is getting information from useChat Context.\n\n onSend?.({ // onSend was recieved as prop and hence we are returning with some value and a boolean tshow\n\t\t\tvalue: text,\n\t\t\ttshow,\n\t\t});\n ```\n", + "description": "## handleSendMessage Function\n\n- This is the function which sends messages further, it Gets text from chat.composer.text where chat is actully using useChat() which is coming from [ChatAPI](./apps/meteor/client/lib/chats/ChatAPI.ts) consisting of multiple functions.\n\n#### The handleSendMessage function is responsible for processing and sending the message to the intended recipients. It performs the following actions:\n\n1. Retrieves the content of the message entered by the user.\n2. Collects additional information needed for sending the message, such as the room/channel ID, sender details, and any associated metadata.\n3. Packages the message data into a suitable format for transmission.\n4. Initiates the process of sending the message, it goes through a **[WebSocket](https://www.wallarm.com/what/a-simple-explanation-of-what-a-websocket-is)** connection.\n5. The message is then transmitted to the server for further processing.\n6. The server handles the message, ensuring it is delivered to the specified room/channel and received by the intended recipients.\n7. Once the message is successfully sent, it may trigger updates to the chat interface, including displaying the sent message in the conversation history.\n\n- Here we have \n ```\n const chat = useChat(); // useChat is a Context using ChatAPI.\n const text = chat.composer?.text ?? ''; //Text is getting information from useChat Context.\n\n onSend?.({ // onSend was recieved as prop and hence we are returning with some value and a boolean tshow\n\t\t\tvalue: text,\n\t\t\ttshow,\n\t\t});\n ```\n", "line": 159 }, { diff --git a/.tours/3---how-a-message-is-sent.tour b/.tours/3---how-a-message-is-sent.tour index 7b1da92b652f..1eb19a985d50 100644 --- a/.tours/3---how-a-message-is-sent.tour +++ b/.tours/3---how-a-message-is-sent.tour @@ -1,76 +1,76 @@ -{ - "$schema": "https://aka.ms/codetour-schema", - "title": "3 - How a Message is sent (The Backend)", - "steps": [ - { - "file": "apps/meteor/client/lib/chats/ChatAPI.ts", - "description": "## The ChatAPI\n\n- **ComposerAPI:** This interface defines methods and properties related to the message composer functionality, such as managing text input, handling selection, wrapping text, inserting text, clearing, focusing, and more. It also includes properties for managing quoted messages, editing mode, recording mode, and microphone permissions.\n\n- **DataAPI:** This interface defines methods for interacting with message and room data. It includes functions for composing and managing messages, finding and getting messages by ID, finding the last and previous own messages, managing drafts, accessing room and subscription information, marking rooms as read, and more.\n\n- **UploadsAPI:** This interface defines methods and properties for managing file uploads. It includes functions for retrieving the list of uploads, subscribing to changes, canceling and wiping failed uploads, and sending files.\n\n- **ChatAPI:** This interface represents the main API object for the Rocket Chat client. It includes properties for user-related information, such as the user ID. It also provides access to the composer, data, uploads, and message editing functionality. Additionally, it exposes methods for performing actions like starting/stopping typing, opening/closing user cards and emoji pickers, and handling various message-related flows (uploading files, sending messages, processing slash commands, editing messages, setting reactions, etc.).", - "line": 106 - }, - { - "file": "apps/meteor/client/lib/chats/ChatAPI.ts", - "description": "## ChatAPI sendMessage\n\n#### Among many properties and functions present in ChatAPI here we have sendMessage.\n\n#### Note that sendMessage is *readonly* implying that it cannot be modified after initialization. Hence Implementing ChatAPI in any class would make it modification possible.\n\n```\n readonly sendMessage: ({ text, tshow }: { text: string; tshow?: boolean }) => Promise<boolean>;\n```", - "line": 146 - }, - { - "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", - "description": "## Implementation of ChatAPI\n\n#### Here *class ChatMessages implements ChatAPI* and above it we have a *type DeepWritable<T>* which removes readonly property from ChatAPI and This allows us to modify those properties\n", - "line": 27 - }, - { - "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", - "description": "## SendMessage -\n\n### Above we have a constructor and here in this.flows we are calling *sendMessage.bind(this,this)* The sendMessage is a function which further makes the meteor call for sending Message Data.", - "line": 160 - }, - { - "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", - "description": "## sendMessage function\n\n### The sendMessage recieves text: string and tshow: boolean properties and returns a Promise, And note that here we are utilizing ChatAPI again.\n\n- Within the sendMessage implementation, there are several checks and actions performed based on the provided parameters and the current state of the chat. These checks ensure that the message is valid and that the necessary actions are taken. The implementation of sendMessage may involve interacting with other methods or properties defined in the ChatAPI interface or its implementation.\n\n- By carefully examining the code and understanding its logic, you can gain a better understanding of how the sendMessage function is modified and what actions it performs in the context of the ChatMessages class.\n\n", - "line": 35, - "selection": { - "start": { - "line": 3, - "character": 5 - }, - "end": { - "line": 3, - "character": 258 - } - } - }, - { - "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", - "description": "## Processing Message Further\n\n### In Rocket.chat Messages are sent to ../api/v1/method.call/sendMessage and this is a meteor method. ", - "line": 62 - }, - { - "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", - "description": "## Calling Meteor method\n\n### This method is taking 2 parameters, one is the route for meteor method(api/v1/method.call/:method) and another parameter is message which is passed on by sendMessage function below.\n\n```\nawait sdk.call('sendMessage', message);\n```", - "line": 32 - }, - { - "file": "apps/meteor/app/utils/client/lib/SDKClient.ts", - "description": "## Meteor.Call\n\n### The sendMessage function in the code triggers a Meteor method by passing a method name and its corresponding parameters. This invocation is responsible for initiating the sendMessage functionality. If by any chance someone calls any other method it would simply throw error.\n\n- There are basically **2 Meteor methods** for sendMessage each of them performing different tasks\n - **The first method is responsible for sending the message data to the database and performing various related tasks. However, there may be a slight delay in the message being rendered in the room or channel. To mitigate this delay, a second Meteor method is executed concurrently in an asynchronous manner.**\n\n - **The second method is specifically designed to facilitate the instant display of the sent message. If this method were disabled or not implemented, there would be a noticeable delay in the message being rendered and visible to the users.**", - "line": 161 - }, - { - "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", - "description": "## Meteor.methods\n\n### In this code, we have defined a ServerMethods interface, which includes a route for the sendMessage method. When the sendMessage action is triggered, an API call is made to the route *api/v1/method.call/sendMessage*. ->\n```\nreturn executeSendMessage(uid, message)\n```\n\n#### This triggers a function *executeSendMessage()* below in a try block which is further responsible for operations", - "line": 110 - }, - { - "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", - "description": "## executeSendMessage\n\n#### - Inside the executeSendMessage() function, we receive the uid (user ID) and message as parameters. This function performs various operations related to the sendMessage action.\n\n#### - One of the key operations is triggering the sendMessage() function, which takes the user, message, room, and a boolean value as parameters. This function is responsible for handling the process of sending the message. It may involve additional validations, processing the message content, interacting with the database, and performing any necessary actions related to sending messages.", - "line": 85 - }, - { - "file": "apps/meteor/app/lib/server/functions/sendMessage.js", - "description": "## Sending message to DB\n\n### In the sendMessage.js file, The sendMessage function that is called from the [executeSendMessage()](./apps/meteor/app/lib/server/methods/sendMessage.ts) function. The purpose of the sendMessage function is to handle the actual sending of messages.\n\n#### Within the sendMessage function, there are several important steps:\n\n1. **Validations**: The function performs validations on the message, ensuring that it meets certain criteria or conditions before proceeding. This helps maintain message integrity and prevents any invalid or malicious content from being sent.\n\n2. **URL Parsing**: The function also includes a step to parse URLs within the message for safety purposes. This ensures that any URLs included in the message are properly handled and do not pose a security risk.\n\n3. **Asynchronous Callback**: The function utilizes an asynchronous callback mechanism, which allows it to run asynchronously and not block the execution flow. This is especially useful when dealing with tasks that may involve network requests or other asynchronous operations.\n\n4. **IMessage Object**: The callback returns an IMessage object, which represents the message being sent. This object may contain various properties and metadata associated with the message.\n\n6. **Storage in Database**: Once the message has passed all validations, it is stored in the database. This ensures that the message is persisted and can be retrieved or displayed at a later time.\n\n#### Finally, the function returns the message object, indicating the successful completion of the sending process.\n- You can see code below it's easy to understand and try to play around with them, Best way to understand working of any code is to make changes in them and undestand.", - "line": 206 - }, - { - "file": "apps/meteor/app/lib/client/methods/sendMessage.ts", - "description": "## The Async sendMessage Api call\n\n#### - As mentioned earlier, there are two Meteor methods for handling the sendMessage functionality and making API Call. The first method handles processing and validation of the message, while the second method addresses the delay in rendering the message on the frontend.\n\n#### - The second Meteor method is designed to ensure instant display of the sent message. When the first method completes successfully, it triggers the execution of the second method in an asynchronous manner. This allows for near-real-time rendering of the message on the user interface, reducing any noticeable delay.\n\n#### And in the end there is a similar callback as it was in previous step which helps in message rendering, The callback return IMessage object which is displayed to user\n\n#### - By separating these two methods, the application can provide a smooth and responsive user experience. The initial processing and validation are performed without blocking the UI, and once that is completed, the message is promptly displayed to the user.\n\n### You Have now successfully sent Message from composer, Note that there is one more API to sendMessage Which is an REST API, Which is currently not being used, if it would be used in future this tour would be updated. Still you can manually sendMessage by making call here [RestAPI Docs](https://developer.rocket.chat/reference/api/rest-api/endpoints/chat-endpoints/send-message) using -curl command, you can go thorugh the DOCS and try sending message on your own", - "line": 14 - } - ] +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "3 - How a Message is sent (The Backend)", + "steps": [ + { + "file": "apps/meteor/client/lib/chats/ChatAPI.ts", + "description": "## The ChatAPI\n\n- **ComposerAPI:** This interface defines methods and properties related to the message composer functionality, such as managing text input, handling selection, wrapping text, inserting text, clearing, focusing, and more. It also includes properties for managing quoted messages, editing mode, recording mode, and microphone permissions.\n\n- **DataAPI:** This interface defines methods for interacting with message and room data. It includes functions for composing and managing messages, finding and getting messages by ID, finding the last and previous own messages, managing drafts, accessing room and subscription information, marking rooms as read, and more.\n\n- **UploadsAPI:** This interface defines methods and properties for managing file uploads. It includes functions for retrieving the list of uploads, subscribing to changes, canceling and wiping failed uploads, and sending files.\n\n- **ChatAPI:** This interface represents the main API object for the Rocket Chat client. It includes properties for user-related information, such as the user ID. It also provides access to the composer, data, uploads, and message editing functionality. Additionally, it exposes methods for performing actions like starting/stopping typing, opening/closing user cards and emoji pickers, and handling various message-related flows (uploading files, sending messages, processing slash commands, editing messages, setting reactions, etc.).", + "line": 106 + }, + { + "file": "apps/meteor/client/lib/chats/ChatAPI.ts", + "description": "## ChatAPI sendMessage\n\n#### Among many properties and functions present in ChatAPI here we have sendMessage.\n\n#### Note that sendMessage is *readonly* implying that it cannot be modified after initialization. Hence Implementing ChatAPI in any class would make it modification possible.\n\n```\n readonly sendMessage: ({ text, tshow }: { text: string; tshow?: boolean }) => Promise<boolean>;\n```", + "line": 146 + }, + { + "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", + "description": "## Implementation of ChatAPI\n\n#### Here *class ChatMessages implements ChatAPI* and above it we have a *type DeepWritable<T>* which removes readonly property from ChatAPI and This allows us to modify those properties\n", + "line": 27 + }, + { + "file": "apps/meteor/app/ui/client/lib/ChatMessages.ts", + "description": "## SendMessage -\n\n### Above we have a constructor and here in this.flows we are calling *sendMessage.bind(this,this)* The sendMessage is a function which further makes the meteor call for sending Message Data.", + "line": 160 + }, + { + "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", + "description": "## sendMessage function\n\n### The sendMessage recieves text: string and tshow: boolean properties and returns a Promise, And note that here we are utilizing ChatAPI again.\n\n- Within the sendMessage implementation, there are several checks and actions performed based on the provided parameters and the current state of the chat. These checks ensure that the message is valid and that the necessary actions are taken. The implementation of sendMessage may involve interacting with other methods or properties defined in the ChatAPI interface or its implementation.\n\n- By carefully examining the code and understanding its logic, you can gain a better understanding of how the sendMessage function is modified and what actions it performs in the context of the ChatMessages class.\n\n", + "line": 35, + "selection": { + "start": { + "line": 3, + "character": 5 + }, + "end": { + "line": 3, + "character": 258 + } + } + }, + { + "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", + "description": "## Processing Message Further\n\n### In Rocket.chat Messages are sent to ../api/v1/method.call/sendMessage and this is a meteor method. ", + "line": 62 + }, + { + "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", + "description": "## Calling Meteor method\n\n### This method is taking 2 parameters, one is the route for meteor method(api/v1/method.call/:method) and another parameter is message which is passed on by sendMessage function below.\n\n```\nawait sdk.call('sendMessage', message);\n```", + "line": 32 + }, + { + "file": "apps/meteor/app/utils/client/lib/SDKClient.ts", + "description": "## Meteor.Call\n\n### The sendMessage function in the code triggers a Meteor method by passing a method name and its corresponding parameters. This invocation is responsible for initiating the sendMessage functionality. If by any chance someone calls any other method it would simply throw error.\n\n- There are basically **2 Meteor methods** for sendMessage each of them performing different tasks\n - **The first method is responsible for sending the message data to the database and performing various related tasks. However, there may be a slight delay in the message being rendered in the room or channel. To mitigate this delay, a second Meteor method is executed concurrently in an asynchronous manner.**\n\n - **The second method is specifically designed to facilitate the instant display of the sent message. If this method were disabled or not implemented, there would be a noticeable delay in the message being rendered and visible to the users.**", + "line": 161 + }, + { + "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", + "description": "## Meteor.methods\n\n### In this code, we have defined a ServerMethods interface, which includes a route for the sendMessage method. When the sendMessage action is triggered, an API call is made to the route *api/v1/method.call/sendMessage*. ->\n```\nreturn executeSendMessage(uid, message)\n```\n\n#### This triggers a function *executeSendMessage()* below in a try block which is further responsible for operations", + "line": 110 + }, + { + "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", + "description": "## executeSendMessage\n\n### - Inside the *executeSendMessage* function, we receive the uid (user ID) and message as parameters. This function performs various operations related to the sendMessage action.\n\n### - One of the key operations is triggering the sendMessage() function, which takes the user, message, room, and a boolean value as parameters. This function is responsible for handling the process of sending the message. It may involve additional validations, processing the message content, interacting with the database, and performing any necessary actions related to sending messages.", + "line": 85 + }, + { + "file": "apps/meteor/app/lib/server/functions/sendMessage.js", + "description": "## Sending message to DB\n\n### In the sendMessage.js file, The sendMessage function that is called from the [executeSendMessage()](./apps/meteor/app/lib/server/methods/sendMessage.ts) function. The purpose of the sendMessage function is to handle the actual sending of messages.\n\n#### Within the sendMessage function, there are several important steps:\n\n1. **Validations**: The function performs validations on the message, ensuring that it meets certain criteria or conditions before proceeding. This helps maintain message integrity and prevents any invalid or malicious content from being sent.\n\n2. **URL Parsing**: The function also includes a step to parse URLs within the message for safety purposes. This ensures that any URLs included in the message are properly handled and do not pose a security risk.\n\n3. **Asynchronous Callback**: The function utilizes an asynchronous callback mechanism, which allows it to run asynchronously and not block the execution flow. This is especially useful when dealing with tasks that may involve network requests or other asynchronous operations.\n\n4. **IMessage Object**: The callback returns an IMessage object, which represents the message being sent. This object may contain various properties and metadata associated with the message.\n\n6. **Storage in Database**: Once the message has passed all validations, it is stored in the database. This ensures that the message is persisted and can be retrieved or displayed at a later time.\n\n#### Finally, the function returns the message object, indicating the successful completion of the sending process.\n- You can see code below it's easy to understand and try to play around with them, Best way to understand working of any code is to make changes in them and undestand.", + "line": 206 + }, + { + "file": "apps/meteor/app/lib/client/methods/sendMessage.ts", + "description": "## The Async sendMessage Api call\n\n### - As mentioned earlier, there are two Meteor methods for handling the sendMessage functionality and making API Call. The first method handles processing and validation of the message, while the second method addresses the delay in rendering the message on the frontend.\n\n### - The second Meteor method is designed to ensure instant display of the sent message. When the first method completes successfully, it triggers the execution of the second method in an asynchronous manner. This allows for near-real-time rendering of the message on the user interface, reducing any noticeable delay.\n\n### And in the end there is a similar callback as it was in previous step which helps in message rendering, The callback return IMessage object which is displayed to user\n\n### - By separating these two methods, the application can provide a smooth and responsive user experience. The initial processing and validation are performed without blocking the UI, and once that is completed, the message is promptly displayed to the user.\n\n### You Have now successfully sent Message from composer, Note that there is one more API to sendMessage Which is an REST API, Which is currently not being used, if it would be used in future this tour would be updated. Still you can manually sendMessage by making call here [RestAPI Docs](https://developer.rocket.chat/reference/api/rest-api/endpoints/chat-endpoints/send-message) using -curl command, you can go thorugh the DOCS and try sending message on your own", + "line": 14 + } + ] } \ No newline at end of file diff --git a/.tours/4---how-to-create-an-endpoint.tour b/.tours/4---how-to-create-an-endpoint.tour index 9bf869e5ce93..ddd30fcbce98 100644 --- a/.tours/4---how-to-create-an-endpoint.tour +++ b/.tours/4---how-to-create-an-endpoint.tour @@ -9,12 +9,12 @@ }, { "file": "apps/meteor/app/api/server/api.ts", - "description": "## REST API\n\n### When using the Rocket.Chat REST API in a production environment, it is important to address security concerns by implementing HTTPS with a valid SSL certificate, regularly expiring and refreshing authorization tokens, and carefully configuring user permissions. Rate limiting is another important aspect to manage API requests, ensure server stability, and prevent abuse. You can enable rate limiting in the Rocket.Chat administration settings and customize it according to your needs. The response headers of API requests with rate limiting enabled provide information about the rate limit constraints, including the number of allowed calls, remaining requests, and reset time. These measures enhance security, protect sensitive data, and promote fair usage of the API.\n\n- [Visit here for REST Docs](https://developer.rocket.chat/reference/api/rest-api)\n\n#### Let us start by looking at example endpoint ie. *api/v1/chat.sendMessage* Here we will understand how endpoint is Created and how can you create a new endpoint", + "description": "## REST API\n\n### When using the Rocket.Chat REST API in a production environment, it is important to address security concerns by implementing HTTPS with a valid SSL certificate, regularly expiring and refreshing authorization tokens, and carefully configuring user permissions. Rate limiting is another important aspect to manage API requests, ensure server stability, and prevent abuse. You can enable rate limiting in the Rocket.Chat administration settings and customize it according to your needs. The response headers of API requests with rate limiting enabled provide information about the rate limit constraints, including the number of allowed calls, remaining requests, and reset time. These measures enhance security, protect sensitive data, and promote fair usage of the API.\n\n#### Let us start by looking at example endpoint ie. *api/v1/chat.sendMessage* Here we will understand how endpoint is Created and how can you create a new endpoint\n\n- [Learn more about REST API](https://developer.rocket.chat/reference/api/rest-api)", "line": 986 }, { "file": "apps/meteor/app/api/server/v1/chat.ts", - "description": "## Example Endpoint\n\n### Here we have \n```\nAPI.v1.addRoute(\n\t'Route.RouteName', // This is the endpoint where request would be sent\n\t{ authRequired: true }, // Here is authRequired is true then user requires to enter their Auth Tokens\n {async post(){ // This is the method call, Here we are sending message hence it requires post method\n <!-- Other Operations -->\n return API.v1.success(); // At the end if everything is successfull a success message is recieved\n },\n },\n);\n```\n\n#### Let us Understand What is *API.v1* .", + "description": "## Example Endpoint for sending Message\n\n#### Visit [here](https://developer.rocket.chat/reference/api/rest-api/endpoints/chat-endpoints/send-message) for Official documentations for sendMessage REST endpoint.\n\n### Here we have \n```\nAPI.v1.addRoute(\n 'route.routeName', // This is the endpoint where request would be sent for example - 'chat.sendMessage'\n { authRequired: true }, // Here is authRequired is true then user requires to enter their Auth Tokens\n {async post(){ // This is the method call, Here we are sending message hence it requires post method\n <!-- Other Operations -->\n return API.v1.success(); // At the end if everything is successfull a success message is recieved\n },\n },\n);\n```\n\n#### Let us Understand What is *API.v1* .", "line": 211 }, { @@ -34,27 +34,27 @@ }, { "file": "apps/meteor/app/api/server/api.ts", - "description": "## CreateApi\n\n### This function helps in creating Api route for example api/v1/exampleRoute\n\n```\nAPI.v1.addRoute()\n```\n- v1 uses instance of APIClass and The APIClass has property addRoute which helps in adding route to Server", + "description": "## CreateApi\n\n### This function helps in creating Api route for example api/v1/abc\n\n```\nAPI.v1.addRoute()\n```\n### - v1 uses instance of APIClass and The APIClass has property addRoute which helps in adding route to Server", "line": 976 }, { "file": "apps/meteor/app/api/server/v1/chat.ts", - "description": "## Calling REST endpoint\n\n### Let us try to call an REST Endpoint for sending message\n- In order to make Api call you need Personal Access Tokens for user authentication. **[Get Token here](https://docs.rocket.chat/use-rocket.chat/user-guides/user-panel/my-account#personal-access-tokens)** (Remember get access token from your own local server)\n\n- To get User-Id go to admin settings then users(or http://localhost:3000/admin/users) select your account and copy unique user id from URL (http://localhost:3000/admin/users/info/u3y2jXw5ayckPciE9) here we have **u3y2jXw5ayckPciE9** as user id, Try finding yours\n\n- After you get you Personal access token Start Rocket Chat server on your local machine and in another terminal enter this command.\n\n```\ncurl -H \"X-Auth-Token: ENTER_YOUR_TOKEN \" \\\n -H \"X-User-Id: ENTER_YOUR_USERID \" \\\n -H \"Content-type:application/json\" \\\n http://localhost:3000/api/v1/chat.sendMessage \\\n -d '{\"message\":{\"rid\":\"GENERAL\", \"msg\":\"Hello From Rocket Chat\"}}'\n```\n- You will get a success response once your request is successful and vice versa\n\n### Similarly you can also create your own REST Endpoint with unique route name and define what it needs to do by adding different funtionalities in it. ", + "description": "## Calling REST endpoint\n### Let us try to call an REST Endpoint for sending message\n### For this you need \n - Personal Access Token\n - UserId\n - Channel/Room Id\n### - Get Personal Access Tokens for user authentication. -> **[Get Token here](https://docs.rocket.chat/use-rocket.chat/user-guides/user-panel/my-account#personal-access-tokens)** (*Note* - get personal access token from your own local server)\n\n### - To get User-Id go to admin settings, search for users(or http://localhost:3000/admin/users) select your account and copy unique user id from URL (http://localhost:3000/admin/users/info/u3y2jXw5ayckPciE9) here we have *u3y2jXw5ayckPciE9* as user id, Try finding yours\n\n### - After you get you Personal access token Start Rocket Chat server on your local machine and in another terminal enter this command.\n\n```sh\ncurl -H \"X-Auth-Token: 'ENTER_YOUR_TOKEN' \" \\\n -H \"X-User-Id: 'ENTER_YOUR_USERID' \" \\\n -H \"Content-type:application/json\" \\\n http://localhost:3000/api/v1/chat.sendMessage \\\n -d '{\"message\":{\"rid\":\"GENERAL\", \"msg\":\"Hello From Rocket Chat\"}}'\n```\n- You will get a success response once your request is successful and vice versa\n\n### Know more about RocketChat REST API [Here](https://developer.rocket.chat/reference/api/rest-api/endpoints/chat-endpoints/send-message)\n\n### Similarly you can also create your own REST Endpoint with unique route name and define what it needs to do by adding different funtionalities in it. ", "line": 211, "selection": { "start": { - "line": 6, - "character": 196 + "line": 15, + "character": 23 }, "end": { - "line": 6, - "character": 213 + "line": 15, + "character": 40 } } }, { "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", - "description": "## RealTime API \n\n### - Using Meteor methods(RealTime API) for endpoints provides real-time data synchronization, server-side data validation, and enhanced security through method-specific permissions, ensuring efficient and secure communication between the client and server.\n\n### - Our real-time API is composed of two elements: Method Calls and Subscriptions. Both of them are supported directly in the websocket connection.", + "description": "## RealTime API \n\n### - Using Meteor methods(RealTime API) for endpoints provides real-time data synchronization, server-side data validation, and enhanced security through method-specific permissions, ensuring efficient and secure communication between the client and server.\n\n### - Our real-time API is composed of two elements: Method Calls and Subscriptions. Both of them are supported directly in the websocket connection. \n\n- **To know more about RealTime API visit [here](https://developer.rocket.chat/reference/api/realtime-api)**", "line": 101 }, { @@ -64,12 +64,12 @@ }, { "file": "apps/meteor/app/lib/server/methods/sendMessage.ts", - "description": "## Using ServerMethod\n\n#### We created sendMessage endpoint with help of interface ServerMethods and here we have a Meteor method and we are defining what this Endpoint will do, for example in this case it is sending message\n\n```\nMeteor.methods<ServerMethods>({\n\tsendMessage(message) { \n // Code for what endpoint should do\n },\n});\n\n```\n\n#### Now let us see how this Endpoint is called", + "description": "## Using ServerMethod\n\n#### We created sendMessage endpoint with help of interface ServerMethods and here we have a Meteor method and we are defining what this Endpoint will do, for example in this case it is sending message\n\n- **[Documentation](https://developer.rocket.chat/reference/api/realtime-api/method-calls) for Method Calls**\n\n```\nMeteor.methods<ServerMethods>({\n\tsendMessage(message) { \n // Code for what endpoint should do\n },\n});\n\n```\n\n#### Now let us see how this Endpoint is called", "line": 110 }, { "file": "apps/meteor/client/lib/chats/flows/sendMessage.ts", - "description": "## The Call method\n\n### The Call method is used to call meteor method and it takes route name and message as argument", + "description": "## Calling Realtime API endpoints\n\n### The sdk.call method is used to call meteor method and it takes route name and message as argument, Rocket chat has created and SDK for managing meteor calls", "line": 32 }, { From d91e480ba986f2fcc66aa740b2d191a4202b3363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Fri, 7 Jul 2023 10:24:31 -0300 Subject: [PATCH 091/149] chore: `GenericMenu` logic to add gap (#29751) --- .../client/components/GenericMenu/GenericMenu.tsx | 11 +++++++++-- .../client/components/GenericMenu/GenericMenuItem.tsx | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/meteor/client/components/GenericMenu/GenericMenu.tsx b/apps/meteor/client/components/GenericMenu/GenericMenu.tsx index 5a9ad9a0877c..aba2e7e0eefb 100644 --- a/apps/meteor/client/components/GenericMenu/GenericMenu.tsx +++ b/apps/meteor/client/components/GenericMenu/GenericMenu.tsx @@ -39,6 +39,9 @@ const GenericMenu = ({ title, icon = 'menu', onAction, ...props }: GenericMenuPr const disabledKeys = itemsList.filter(({ disabled }) => disabled).map(({ id }) => id); const handleAction = useHandleMenuAction(itemsList || []); + const hasIcon = itemsList.some(({ icon }) => icon); + const handleItems = (items: GenericMenuItemProps[]) => (hasIcon ? items.map((item) => ({ ...item, gap: !item.icon })) : items); + return ( <> {sections && ( @@ -50,7 +53,11 @@ const GenericMenu = ({ title, icon = 'menu', onAction, ...props }: GenericMenuPr {...props} > {sections.map(({ title, items }, key) => ( - <MenuSection title={typeof title === 'string' && t.has(title) ? t(title) : title} items={items} key={`${title}-${key}`}> + <MenuSection + title={typeof title === 'string' && t.has(title) ? t(title) : title} + items={handleItems(items)} + key={`${title}-${key}`} + > {(item) => ( <MenuItem key={item.id}> <GenericMenuItem {...item} /> @@ -68,7 +75,7 @@ const GenericMenu = ({ title, icon = 'menu', onAction, ...props }: GenericMenuPr {...(disabledKeys && { disabledKeys })} {...props} > - {items.map((item) => ( + {handleItems(items).map((item) => ( <MenuItem key={item.id}> <GenericMenuItem {...item} /> </MenuItem> diff --git a/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx b/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx index ca56b79de258..ae79d5a4a78b 100644 --- a/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx +++ b/apps/meteor/client/components/GenericMenu/GenericMenuItem.tsx @@ -11,10 +11,12 @@ export type GenericMenuItemProps = { status?: ReactNode; disabled?: boolean; description?: ReactNode; + gap?: boolean; }; -const GenericMenuItem = ({ icon, content, addon, status }: GenericMenuItemProps) => ( +const GenericMenuItem = ({ icon, content, addon, status, gap }: GenericMenuItemProps) => ( <> + {gap && <MenuItemColumn />} {icon && <MenuItemIcon name={icon} />} {status && <MenuItemColumn>{status}</MenuItemColumn>} {content && <MenuItemContent>{content}</MenuItemContent>} From e01bbcca5410374450229dfaa2d64d739dda3a10 Mon Sep 17 00:00:00 2001 From: Jayesh Jain <79307894+jayesh-jain252@users.noreply.github.com> Date: Fri, 7 Jul 2023 21:00:07 +0530 Subject: [PATCH 092/149] fix: mentions and emojis inside bold, italic and strikethrough (#29391) --- .changeset/mentions-emoji-emphasis.md | 6 ++ .../markup/elements/BoldSpan.tsx | 56 ++++++++++----- .../markup/elements/ItalicSpan.tsx | 56 ++++++++++----- .../markup/elements/StrikeSpan.tsx | 56 ++++++++++----- packages/gazzodown/src/elements/BoldSpan.tsx | 60 +++++++++++----- .../gazzodown/src/elements/ImageElement.tsx | 4 +- .../gazzodown/src/elements/ItalicSpan.tsx | 68 ++++++++++++------- .../gazzodown/src/elements/StrikeSpan.tsx | 68 ++++++++++++------- yarn.lock | 6 +- 9 files changed, 260 insertions(+), 120 deletions(-) create mode 100644 .changeset/mentions-emoji-emphasis.md diff --git a/.changeset/mentions-emoji-emphasis.md b/.changeset/mentions-emoji-emphasis.md new file mode 100644 index 000000000000..044fe46b00e0 --- /dev/null +++ b/.changeset/mentions-emoji-emphasis.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/gazzodown': minor +--- + +Fixed mentions and emojis inside inside bold, italic or strikethrough texts + diff --git a/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/BoldSpan.tsx b/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/BoldSpan.tsx index 362fe269a652..1a7a24e2fd17 100644 --- a/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/BoldSpan.tsx +++ b/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/BoldSpan.tsx @@ -4,6 +4,7 @@ import type * as MessageParser from '@rocket.chat/message-parser'; import ItalicSpan from './ItalicSpan'; import LinkSpan from './LinkSpan'; import StrikeSpan from './StrikeSpan'; +import EmojiSpan from './EmojiSpan'; const styles = StyleSheet.create({ bold: { @@ -11,31 +12,52 @@ const styles = StyleSheet.create({ }, }); +type MessageBlock = + | MessageParser.Emoji + | MessageParser.ChannelMention + | MessageParser.UserMention + | MessageParser.Link + | MessageParser.MarkupExcluding<MessageParser.Bold>; + type BoldSpanProps = { - children: (MessageParser.Link | MessageParser.MarkupExcluding<MessageParser.Bold>)[]; + children: MessageBlock[]; }; const BoldSpan = ({ children }: BoldSpanProps) => ( - <View style={styles.bold}> + <> {children.map((child, index) => { - switch (child.type) { - case 'LINK': - return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />; + if (child.type === 'LINK' || child.type === 'PLAIN_TEXT' || child.type === 'ITALIC' || child.type === 'STRIKE') { + return ( + <View style={styles.bold} key={index}> + {renderBlockComponent(child, index)} + </View> + ); + } + return renderBlockComponent(child, index); + })} + </> +); - case 'PLAIN_TEXT': - return <Text key={index}>{child.value}</Text>; +const renderBlockComponent = (child: MessageBlock, index: number) => { + switch (child.type) { + case 'LINK': + return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />; - case 'STRIKE': - return <StrikeSpan key={index} children={child.value} />; + case 'PLAIN_TEXT': + return <Text key={index}>{child.value}</Text>; - case 'ITALIC': - return <ItalicSpan key={index} children={child.value} />; + case 'STRIKE': + return <StrikeSpan key={index} children={child.value} />; - default: - return null; - } - })} - </View> -); + case 'ITALIC': + return <ItalicSpan key={index} children={child.value} />; + + case 'EMOJI': + return <EmojiSpan key={index} {...child} />; + + default: + return null; + } +}; export default BoldSpan; diff --git a/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/ItalicSpan.tsx b/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/ItalicSpan.tsx index d970cde92f40..1913ed98ee76 100644 --- a/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/ItalicSpan.tsx +++ b/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/ItalicSpan.tsx @@ -4,6 +4,7 @@ import type * as MessageParser from '@rocket.chat/message-parser'; import BoldSpan from './BoldSpan'; import LinkSpan from './LinkSpan'; import StrikeSpan from './StrikeSpan'; +import EmojiSpan from './EmojiSpan'; const styles = StyleSheet.create({ italic: { @@ -11,31 +12,52 @@ const styles = StyleSheet.create({ }, }); +type MessageBlock = + | MessageParser.Emoji + | MessageParser.ChannelMention + | MessageParser.UserMention + | MessageParser.Link + | MessageParser.MarkupExcluding<MessageParser.Italic>; + type ItalicSpanProps = { - children: (MessageParser.Link | MessageParser.MarkupExcluding<MessageParser.Italic>)[]; + children: MessageBlock[]; }; const ItalicSpan = ({ children }: ItalicSpanProps) => ( - <View style={styles.italic}> + <> {children.map((child, index) => { - switch (child.type) { - case 'LINK': - return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />; + if (child.type === 'LINK' || child.type === 'PLAIN_TEXT' || child.type === 'STRIKE' || child.type === 'BOLD') { + return ( + <View style={styles.italic} key={index}> + {renderBlockComponent(child, index)} + </View> + ); + } + return renderBlockComponent(child, index); + })} + </> +); - case 'PLAIN_TEXT': - return <Text key={index}>{child.value}</Text>; +const renderBlockComponent = (child: MessageBlock, index: number) => { + switch (child.type) { + case 'LINK': + return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />; - case 'STRIKE': - return <StrikeSpan key={index} children={child.value} />; + case 'PLAIN_TEXT': + return <Text key={index}>{child.value}</Text>; - case 'BOLD': - return <BoldSpan key={index} children={child.value} />; + case 'STRIKE': + return <StrikeSpan key={index} children={child.value} />; - default: - return null; - } - })} - </View> -); + case 'BOLD': + return <BoldSpan key={index} children={child.value} />; + + case 'EMOJI': + return <EmojiSpan key={index} {...child} />; + + default: + return null; + } +}; export default ItalicSpan; diff --git a/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/StrikeSpan.tsx b/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/StrikeSpan.tsx index 0ce411d03dc3..fe29ba2b6fbc 100644 --- a/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/StrikeSpan.tsx +++ b/ee/packages/pdf-worker/src/templates/ChatTranscript/markup/elements/StrikeSpan.tsx @@ -4,6 +4,7 @@ import type * as MessageParser from '@rocket.chat/message-parser'; import ItalicSpan from './ItalicSpan'; import LinkSpan from './LinkSpan'; import BoldSpan from './BoldSpan'; +import EmojiSpan from './EmojiSpan'; const styles = StyleSheet.create({ strike: { @@ -11,31 +12,52 @@ const styles = StyleSheet.create({ }, }); +type MessageBlock = + | MessageParser.Emoji + | MessageParser.ChannelMention + | MessageParser.UserMention + | MessageParser.Link + | MessageParser.MarkupExcluding<MessageParser.Strike>; + type StrikeSpanProps = { - children: (MessageParser.Link | MessageParser.MarkupExcluding<MessageParser.Strike>)[]; + children: MessageBlock[]; }; const StrikeSpan = ({ children }: StrikeSpanProps) => ( - <View style={styles.strike}> + <> {children.map((child, index) => { - switch (child.type) { - case 'LINK': - return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />; + if (child.type === 'LINK' || child.type === 'PLAIN_TEXT' || child.type === 'ITALIC' || child.type === 'BOLD') { + return ( + <View style={styles.strike} key={index}> + {renderBlockComponent(child, index)} + </View> + ); + } + return renderBlockComponent(child, index); + })} + </> +); - case 'PLAIN_TEXT': - return <Text key={index}>{child.value}</Text>; +const renderBlockComponent = (child: MessageBlock, index: number) => { + switch (child.type) { + case 'LINK': + return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />; - case 'BOLD': - return <BoldSpan key={index} children={child.value} />; + case 'PLAIN_TEXT': + return <Text key={index}>{child.value}</Text>; - case 'ITALIC': - return <ItalicSpan key={index} children={child.value} />; + case 'ITALIC': + return <ItalicSpan key={index} children={child.value} />; - default: - return null; - } - })} - </View> -); + case 'BOLD': + return <BoldSpan key={index} children={child.value} />; + + case 'EMOJI': + return <EmojiSpan key={index} {...child} />; + + default: + return null; + } +}; export default StrikeSpan; diff --git a/packages/gazzodown/src/elements/BoldSpan.tsx b/packages/gazzodown/src/elements/BoldSpan.tsx index b10611aa74df..2cc6c60140c2 100644 --- a/packages/gazzodown/src/elements/BoldSpan.tsx +++ b/packages/gazzodown/src/elements/BoldSpan.tsx @@ -1,36 +1,62 @@ import type * as MessageParser from '@rocket.chat/message-parser'; import type { ReactElement } from 'react'; +import EmojiElement from '../emoji/EmojiElement'; +import ChannelMentionElement from '../mentions/ChannelMentionElement'; +import UserMentionElement from '../mentions/UserMentionElement'; import ItalicSpan from './ItalicSpan'; import LinkSpan from './LinkSpan'; import PlainSpan from './PlainSpan'; import StrikeSpan from './StrikeSpan'; +type MessageBlock = + | MessageParser.Emoji + | MessageParser.ChannelMention + | MessageParser.UserMention + | MessageParser.Link + | MessageParser.MarkupExcluding<MessageParser.Bold>; + type BoldSpanProps = { - children: (MessageParser.Link | MessageParser.MarkupExcluding<MessageParser.Bold>)[]; + children: MessageBlock[]; }; const BoldSpan = ({ children }: BoldSpanProps): ReactElement => ( - <strong> + <> {children.map((block, index) => { - switch (block.type) { - case 'LINK': - return <LinkSpan key={index} href={block.value.src.value} label={block.value.label} />; + if (block.type === 'LINK' || block.type === 'PLAIN_TEXT' || block.type === 'STRIKE' || block.type === 'ITALIC') { + return <strong key={index}>{renderBlockComponent(block, index)}</strong>; + } + return renderBlockComponent(block, index); + })} + </> +); - case 'PLAIN_TEXT': - return <PlainSpan key={index} text={block.value} />; +const renderBlockComponent = (block: MessageBlock, index: number): ReactElement | null => { + switch (block.type) { + case 'EMOJI': + return <EmojiElement key={index} {...block} />; - case 'STRIKE': - return <StrikeSpan key={index} children={block.value} />; + case 'MENTION_USER': + return <UserMentionElement key={index} mention={block.value.value} />; - case 'ITALIC': - return <ItalicSpan key={index} children={block.value} />; + case 'MENTION_CHANNEL': + return <ChannelMentionElement key={index} mention={block.value.value} />; - default: - return null; - } - })} - </strong> -); + case 'PLAIN_TEXT': + return <PlainSpan key={index} text={block.value} />; + + case 'LINK': + return <LinkSpan key={index} href={block.value.src.value} label={block.value.label} />; + + case 'STRIKE': + return <StrikeSpan key={index} children={block.value} />; + + case 'ITALIC': + return <ItalicSpan key={index} children={block.value} />; + + default: + return null; + } +}; export default BoldSpan; diff --git a/packages/gazzodown/src/elements/ImageElement.tsx b/packages/gazzodown/src/elements/ImageElement.tsx index 61b4cc18e109..c42bba91f884 100644 --- a/packages/gazzodown/src/elements/ImageElement.tsx +++ b/packages/gazzodown/src/elements/ImageElement.tsx @@ -1,7 +1,9 @@ import type * as MessageParser from '@rocket.chat/message-parser'; import { ReactElement, useMemo } from 'react'; -const flattenMarkup = (markup: MessageParser.Markup | MessageParser.Link): string => { +const flattenMarkup = ( + markup: MessageParser.Markup | MessageParser.Link | MessageParser.Emoji | MessageParser.ChannelMention | MessageParser.UserMention, +): string => { switch (markup.type) { case 'PLAIN_TEXT': return markup.value; diff --git a/packages/gazzodown/src/elements/ItalicSpan.tsx b/packages/gazzodown/src/elements/ItalicSpan.tsx index bc0e14098327..82bfddbe0ab1 100644 --- a/packages/gazzodown/src/elements/ItalicSpan.tsx +++ b/packages/gazzodown/src/elements/ItalicSpan.tsx @@ -1,42 +1,62 @@ import type * as MessageParser from '@rocket.chat/message-parser'; import type { ReactElement } from 'react'; +import EmojiElement from '../emoji/EmojiElement'; +import ChannelMentionElement from '../mentions/ChannelMentionElement'; +import UserMentionElement from '../mentions/UserMentionElement'; import BoldSpan from './BoldSpan'; import LinkSpan from './LinkSpan'; import PlainSpan from './PlainSpan'; import StrikeSpan from './StrikeSpan'; +type MessageBlock = + | MessageParser.Emoji + | MessageParser.ChannelMention + | MessageParser.UserMention + | MessageParser.Link + | MessageParser.MarkupExcluding<MessageParser.Italic>; + type ItalicSpanProps = { - children: (MessageParser.Link | MessageParser.MarkupExcluding<MessageParser.Italic>)[]; + children: MessageBlock[]; }; const ItalicSpan = ({ children }: ItalicSpanProps): ReactElement => ( - <em> + <> {children.map((block, index) => { - switch (block.type) { - case 'LINK': - return ( - <LinkSpan - key={index} - href={block.value.src.value} - label={Array.isArray(block.value.label) ? block.value.label : [block.value.label]} - /> - ); - - case 'PLAIN_TEXT': - return <PlainSpan key={index} text={block.value} />; - - case 'STRIKE': - return <StrikeSpan key={index} children={block.value} />; - - case 'BOLD': - return <BoldSpan key={index} children={block.value} />; - - default: - return null; + if (block.type === 'LINK' || block.type === 'PLAIN_TEXT' || block.type === 'STRIKE' || block.type === 'BOLD') { + return <em key={index}>{renderBlockComponent(block, index)}</em>; } + return renderBlockComponent(block, index); })} - </em> + </> ); +const renderBlockComponent = (block: MessageBlock, index: number): ReactElement | null => { + switch (block.type) { + case 'EMOJI': + return <EmojiElement key={index} {...block} />; + + case 'MENTION_USER': + return <UserMentionElement key={index} mention={block.value.value} />; + + case 'MENTION_CHANNEL': + return <ChannelMentionElement key={index} mention={block.value.value} />; + + case 'PLAIN_TEXT': + return <PlainSpan key={index} text={block.value} />; + + case 'LINK': + return <LinkSpan key={index} href={block.value.src.value} label={block.value.label} />; + + case 'STRIKE': + return <StrikeSpan key={index} children={block.value} />; + + case 'BOLD': + return <BoldSpan key={index} children={block.value} />; + + default: + return null; + } +}; + export default ItalicSpan; diff --git a/packages/gazzodown/src/elements/StrikeSpan.tsx b/packages/gazzodown/src/elements/StrikeSpan.tsx index e200e1154839..302b226d7c19 100644 --- a/packages/gazzodown/src/elements/StrikeSpan.tsx +++ b/packages/gazzodown/src/elements/StrikeSpan.tsx @@ -1,42 +1,62 @@ import type * as MessageParser from '@rocket.chat/message-parser'; import type { ReactElement } from 'react'; +import EmojiElement from '../emoji/EmojiElement'; +import ChannelMentionElement from '../mentions/ChannelMentionElement'; +import UserMentionElement from '../mentions/UserMentionElement'; import BoldSpan from './BoldSpan'; import ItalicSpan from './ItalicSpan'; import LinkSpan from './LinkSpan'; import PlainSpan from './PlainSpan'; +type MessageBlock = + | MessageParser.Emoji + | MessageParser.ChannelMention + | MessageParser.UserMention + | MessageParser.Link + | MessageParser.MarkupExcluding<MessageParser.Strike>; + type StrikeSpanProps = { - children: (MessageParser.Link | MessageParser.MarkupExcluding<MessageParser.Strike>)[]; + children: MessageBlock[]; }; const StrikeSpan = ({ children }: StrikeSpanProps): ReactElement => ( - <del> + <> {children.map((block, index) => { - switch (block.type) { - case 'LINK': - return ( - <LinkSpan - key={index} - href={block.value.src.value} - label={Array.isArray(block.value.label) ? block.value.label : [block.value.label]} - /> - ); - - case 'PLAIN_TEXT': - return <PlainSpan key={index} text={block.value} />; - - case 'BOLD': - return <BoldSpan key={index} children={block.value} />; - - case 'ITALIC': - return <ItalicSpan key={index} children={block.value} />; - - default: - return null; + if (block.type === 'LINK' || block.type === 'PLAIN_TEXT' || block.type === 'ITALIC' || block.type === 'BOLD') { + return <del key={index}>{renderBlockComponent(block, index)}</del>; } + return renderBlockComponent(block, index); })} - </del> + </> ); +const renderBlockComponent = (block: MessageBlock, index: number): ReactElement | null => { + switch (block.type) { + case 'EMOJI': + return <EmojiElement key={index} {...block} />; + + case 'MENTION_USER': + return <UserMentionElement key={index} mention={block.value.value} />; + + case 'MENTION_CHANNEL': + return <ChannelMentionElement key={index} mention={block.value.value} />; + + case 'PLAIN_TEXT': + return <PlainSpan key={index} text={block.value} />; + + case 'LINK': + return <LinkSpan key={index} href={block.value.src.value} label={block.value.label} />; + + case 'ITALIC': + return <ItalicSpan key={index} children={block.value} />; + + case 'BOLD': + return <BoldSpan key={index} children={block.value} />; + + default: + return null; + } +}; + export default StrikeSpan; diff --git a/yarn.lock b/yarn.lock index df384064d532..78715030ce8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9950,11 +9950,11 @@ __metadata: linkType: hard "@rocket.chat/message-parser@npm:next": - version: 0.32.0-dev.277 - resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.277" + version: 0.32.0-dev.296 + resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.296" dependencies: tldts: ~5.7.112 - checksum: c1e34dbe2dd23142c565088a86f4243501364a95de456e0b8add3cec78cd44545747b4ed7add7a1403667c14193a1bac52d2a98add85f0cf3cd9844fc9c03095 + checksum: 2a40090b0b5b94e531da3bd1433c6f60b98f19b85eb9ca81db8706fcf360d8245c97fde2257a6c39e9be7fa4a630eede5abf679847ef3c923594c7156f8eb334 languageName: node linkType: hard From 4eb944cdeb992937a3e5482875fb0211d0303822 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Fri, 7 Jul 2023 13:42:33 -0300 Subject: [PATCH 093/149] refactor: Changed `getUserPreference` to `useUserPreference` on `useVoipSounds` (#29744) --- .../client/providers/CallProvider/hooks/useVoipSounds.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts b/apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts index 44e9f19e72e6..5ba847cae8d2 100644 --- a/apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts +++ b/apps/meteor/client/providers/CallProvider/hooks/useVoipSounds.ts @@ -1,18 +1,15 @@ -import { useCustomSound, useUser } from '@rocket.chat/ui-contexts'; +import { useCustomSound, useUserPreference } from '@rocket.chat/ui-contexts'; import { useMemo } from 'react'; -import { getUserPreference } from '../../../../app/utils/client'; - type VoipSound = 'telephone' | 'outbound-call-ringing' | 'call-ended'; export const useVoipSounds = () => { const { play, pause } = useCustomSound(); - const user = useUser(); + const audioVolume = useUserPreference<number>('notificationsSoundVolume', 100) || 100; return useMemo( () => ({ play: (soundId: VoipSound, loop = true) => { - const audioVolume = getUserPreference(user, 'notificationsSoundVolume', 100) as number; play(soundId, { volume: Number((audioVolume / 100).toPrecision(2)), loop, @@ -24,6 +21,6 @@ export const useVoipSounds = () => { pause('outbound-call-ringing'); }, }), - [play, pause, user], + [play, pause, audioVolume], ); }; From e6f905d58d9d2326dbf8cf67d6455637cc10dc2b Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Fri, 7 Jul 2023 13:55:41 -0300 Subject: [PATCH 094/149] chore: bump fuselage deps (#29739) --- packages/uikit-playground/package.json | 2 +- yarn.lock | 3598 ++---------------------- 2 files changed, 215 insertions(+), 3385 deletions(-) diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index c60b02b9ca00..39933f5e16ed 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -14,7 +14,7 @@ "@codemirror/lang-json": "^6.0.1", "@codemirror/tooltip": "^0.19.16", "@lezer/highlight": "^1.1.6", - "@rocket.chat/css-in-js": "^0.31.12", + "@rocket.chat/css-in-js": "next", "@rocket.chat/fuselage": "next", "@rocket.chat/fuselage-hooks": "next", "@rocket.chat/fuselage-polyfills": "next", diff --git a/yarn.lock b/yarn.lock index 78715030ce8b..539e35e375fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -957,16 +957,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": - version: 7.21.4 - resolution: "@babel/code-frame@npm:7.21.4" - dependencies: - "@babel/highlight": ^7.18.6 - checksum: e5390e6ec1ac58dcef01d4f18eaf1fd2f1325528661ff6d4a5de8979588b9f5a8e852a54a91b923846f7a5c681b217f0a45c2524eb9560553160cd963b7d592c - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.22.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.5, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": version: 7.22.5 resolution: "@babel/code-frame@npm:7.22.5" dependencies: @@ -975,21 +966,7 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/compat-data@npm:7.21.4" - checksum: 5f8b98c66f2ffba9f3c3a82c0cf354c52a0ec5ad4797b370dc32bdcd6e136ac4febe5e93d76ce76e175632e2dbf6ce9f46319aa689fcfafa41b6e49834fa4b66 - languageName: node - linkType: hard - -"@babel/compat-data@npm:^7.21.5": - version: 7.21.7 - resolution: "@babel/compat-data@npm:7.21.7" - checksum: 28747eb3fc084d088ba2db0336f52118cfa730a57bdbac81630cae1f38ad0336605b95b3390325937802f344e0b7fa25e2f1b67e3ee2d7383b877f88dee0e51c - languageName: node - linkType: hard - -"@babel/compat-data@npm:^7.22.5": +"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.5": version: 7.22.5 resolution: "@babel/compat-data@npm:7.22.5" checksum: eb1a47ebf79ae268b4a16903e977be52629339806e248455eb9973897c503a04b701f36a9de64e19750d6e081d0561e77a514c8dc470babbeba59ae94298ed18 @@ -1020,30 +997,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.1.0, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.7.5": - version: 7.21.4 - resolution: "@babel/core@npm:7.21.4" - dependencies: - "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.4 - "@babel/helper-compilation-targets": ^7.21.4 - "@babel/helper-module-transforms": ^7.21.2 - "@babel/helpers": ^7.21.0 - "@babel/parser": ^7.21.4 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.4 - "@babel/types": ^7.21.4 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.2 - json5: ^2.2.2 - semver: ^6.3.0 - checksum: a3beebb2cc79908a02f27a07dc381bcb34e8ecc58fa99f568ad0934c49e12111fc977ee9c5b51eb7ea2da66f63155d37c4dd96b6472eaeecfc35843ccb56bf3d - languageName: node - linkType: hard - -"@babel/core@npm:^7.20.5, @babel/core@npm:~7.22.5": +"@babel/core@npm:^7.1.0, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.20.5, @babel/core@npm:^7.21.4, @babel/core@npm:^7.7.5, @babel/core@npm:~7.22.5": version: 7.22.5 resolution: "@babel/core@npm:7.22.5" dependencies: @@ -1066,29 +1020,6 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.21.4": - version: 7.21.8 - resolution: "@babel/core@npm:7.21.8" - dependencies: - "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.5 - "@babel/helper-compilation-targets": ^7.21.5 - "@babel/helper-module-transforms": ^7.21.5 - "@babel/helpers": ^7.21.5 - "@babel/parser": ^7.21.8 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.2 - json5: ^2.2.2 - semver: ^6.3.0 - checksum: f28118447355af2a90bd340e2e60699f94c8020517eba9b71bf8ebff62fa9e00d63f076e033f9dfb97548053ad62ada45fafb0d96584b1a90e8aef5a3b8241b1 - languageName: node - linkType: hard - "@babel/eslint-parser@npm:^7.22.5, @babel/eslint-parser@npm:~7.22.5": version: 7.22.5 resolution: "@babel/eslint-parser@npm:7.22.5" @@ -1103,31 +1034,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.21.4, @babel/generator@npm:^7.7.2": - version: 7.21.4 - resolution: "@babel/generator@npm:7.21.4" - dependencies: - "@babel/types": ^7.21.4 - "@jridgewell/gen-mapping": ^0.3.2 - "@jridgewell/trace-mapping": ^0.3.17 - jsesc: ^2.5.1 - checksum: 9ffbb526a53bb8469b5402f7b5feac93809b09b2a9f82fcbfcdc5916268a65dae746a1f2479e03ba4fb0776facd7c892191f63baa61ab69b2cfdb24f7b92424d - languageName: node - linkType: hard - -"@babel/generator@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/generator@npm:7.21.5" - dependencies: - "@babel/types": ^7.21.5 - "@jridgewell/gen-mapping": ^0.3.2 - "@jridgewell/trace-mapping": ^0.3.17 - jsesc: ^2.5.1 - checksum: 78af737b9dd701d4c657f9731880430fa1c177767b562f4e8a330a7fe72a4abe857e3d24de4e6d9dafc1f6a11f894162d27e523d7e5948ff9e3925a0ce9867c4 - languageName: node - linkType: hard - -"@babel/generator@npm:^7.22.5": +"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.22.5, @babel/generator@npm:^7.7.2": version: 7.22.5 resolution: "@babel/generator@npm:7.22.5" dependencies: @@ -1139,16 +1046,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: 88ccd15ced475ef2243fdd3b2916a29ea54c5db3cd0cfabf9d1d29ff6e63b7f7cd1c27264137d7a40ac2e978b9b9a542c332e78f40eb72abe737a7400788fc1b - languageName: node - linkType: hard - -"@babel/helper-annotate-as-pure@npm:^7.22.5": +"@babel/helper-annotate-as-pure@npm:^7.18.6, @babel/helper-annotate-as-pure@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" dependencies: @@ -1157,16 +1055,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.18.6" - dependencies: - "@babel/helper-explode-assignable-expression": ^7.18.6 - "@babel/types": ^7.18.6 - checksum: c4d71356e0adbc20ce9fe7c1e1181ff65a78603f8bba7615745f0417fed86bad7dc0a54a840bc83667c66709b3cb3721edcb9be0d393a298ce4e9eb6d085f3c1 - languageName: node - linkType: hard - "@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.5" @@ -1176,37 +1064,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/helper-compilation-targets@npm:7.21.4" - dependencies: - "@babel/compat-data": ^7.21.4 - "@babel/helper-validator-option": ^7.21.0 - browserslist: ^4.21.3 - lru-cache: ^5.1.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: bf9c7d3e7e6adff9222c05d898724cd4ee91d7eb9d52222c7ad2a22955620c2872cc2d9bdf0e047df8efdb79f4e3af2a06b53f509286145feccc4d10ddc318be - languageName: node - linkType: hard - -"@babel/helper-compilation-targets@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-compilation-targets@npm:7.21.5" - dependencies: - "@babel/compat-data": ^7.21.5 - "@babel/helper-validator-option": ^7.21.0 - browserslist: ^4.21.3 - lru-cache: ^5.1.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 0edecb9c970ddc22ebda1163e77a7f314121bef9e483e0e0d9a5802540eed90d5855b6bf9bce03419b35b2e07c323e62d0353b153fa1ca34f17dbba897a83c25 - languageName: node - linkType: hard - -"@babel/helper-compilation-targets@npm:^7.22.5": +"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-compilation-targets@npm:7.22.5" dependencies: @@ -1221,44 +1079,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.17.6, @babel/helper-create-class-features-plugin@npm:^7.18.6": - version: 7.21.8 - resolution: "@babel/helper-create-class-features-plugin@npm:7.21.8" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-member-expression-to-functions": ^7.21.5 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-replace-supers": ^7.21.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/helper-split-export-declaration": ^7.18.6 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 26b978bd2e741259c0f4a1cc37521ad58728c50d28fe2fc8041d4381497e13a0b686a10e170246855eaf3af08886862e9d93fc27994ef914e13fca0d73efdcb8 - languageName: node - linkType: hard - -"@babel/helper-create-class-features-plugin@npm:^7.21.0": - version: 7.21.4 - resolution: "@babel/helper-create-class-features-plugin@npm:7.21.4" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-member-expression-to-functions": ^7.21.0 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-replace-supers": ^7.20.7 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/helper-split-export-declaration": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 9123ca80a4894aafdb1f0bc08e44f6be7b12ed1fbbe99c501b484f9b1a17ff296b6c90c18c222047d53c276f07f17b4de857946fa9d0aa207023b03e4cc716f2 - languageName: node - linkType: hard - -"@babel/helper-create-class-features-plugin@npm:^7.22.5": +"@babel/helper-create-class-features-plugin@npm:^7.17.6, @babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-create-class-features-plugin@npm:7.22.5" dependencies: @@ -1277,32 +1098,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.18.6": - version: 7.21.8 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.8" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - regexpu-core: ^5.3.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 04a686b5897c86339395894c0a9a1ffdce2facaba5173ce7b0a894f775f984ba70d2fa227d309f2be54f7f1286ebd1a0a7051a8b1829521595e4064ee062af65 - languageName: node - linkType: hard - -"@babel/helper-create-regexp-features-plugin@npm:^7.20.5": - version: 7.21.4 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.4" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - regexpu-core: ^5.3.1 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 78334865db2cd1d64d103bd0d96dee2818b0387d10aa973c084e245e829df32652bca530803e397b7158af4c02b9b21d5a9601c29bdfbb8d54a3d4ad894e067b - languageName: node - linkType: hard - -"@babel/helper-create-regexp-features-plugin@npm:^7.22.5": +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.5" dependencies: @@ -1333,22 +1129,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.3": - version: 0.3.3 - resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" - dependencies: - "@babel/helper-compilation-targets": ^7.17.7 - "@babel/helper-plugin-utils": ^7.16.7 - debug: ^4.1.1 - lodash.debounce: ^4.0.8 - resolve: ^1.14.2 - semver: ^6.1.2 - peerDependencies: - "@babel/core": ^7.4.0-0 - checksum: 8e3fe75513302e34f6d92bd67b53890e8545e6c5bca8fe757b9979f09d68d7e259f6daea90dc9e01e332c4f8781bda31c5fe551c82a277f9bc0bec007aed497c - languageName: node - linkType: hard - "@babel/helper-define-polyfill-provider@npm:^0.4.0": version: 0.4.0 resolution: "@babel/helper-define-polyfill-provider@npm:0.4.0" @@ -1365,13 +1145,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.18.9, @babel/helper-environment-visitor@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-environment-visitor@npm:7.21.5" - checksum: e436af7b62956e919066448013a3f7e2cd0b51010c26c50f790124dcd350be81d5597b4e6ed0a4a42d098a27de1e38561cd7998a116a42e7899161192deac9a6 - languageName: node - linkType: hard - "@babel/helper-environment-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-environment-visitor@npm:7.22.5" @@ -1379,25 +1152,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-explode-assignable-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: 225cfcc3376a8799023d15dc95000609e9d4e7547b29528c7f7111a0e05493ffb12c15d70d379a0bb32d42752f340233c4115bded6d299bc0c3ab7a12be3d30f - languageName: node - linkType: hard - -"@babel/helper-function-name@npm:^7.18.9, @babel/helper-function-name@npm:^7.19.0, @babel/helper-function-name@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-function-name@npm:7.21.0" - dependencies: - "@babel/template": ^7.20.7 - "@babel/types": ^7.21.0 - checksum: d63e63c3e0e3e8b3138fa47b0cd321148a300ef12b8ee951196994dcd2a492cc708aeda94c2c53759a5c9177fffaac0fd8778791286746f72a000976968daf4e - languageName: node - linkType: hard - "@babel/helper-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-function-name@npm:7.22.5" @@ -1408,15 +1162,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-hoist-variables@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f - languageName: node - linkType: hard - "@babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" @@ -1426,24 +1171,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.20.7, @babel/helper-member-expression-to-functions@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-member-expression-to-functions@npm:7.21.0" - dependencies: - "@babel/types": ^7.21.0 - checksum: 49cbb865098195fe82ba22da3a8fe630cde30dcd8ebf8ad5f9a24a2b685150c6711419879cf9d99b94dad24cff9244d8c2a890d3d7ec75502cd01fe58cff5b5d - languageName: node - linkType: hard - -"@babel/helper-member-expression-to-functions@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-member-expression-to-functions@npm:7.21.5" - dependencies: - "@babel/types": ^7.21.5 - checksum: c404b4a0271c640b7dc8c34af7b683c70a43200259e02330cfc02e79e6b271e9227f35554cd6ad015eabcfa1fea75b9d0b87b69f3d1e6c2af6edd224060b1732 - languageName: node - linkType: hard - "@babel/helper-member-expression-to-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-member-expression-to-functions@npm:7.22.5" @@ -1453,16 +1180,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/helper-module-imports@npm:7.21.4" - dependencies: - "@babel/types": ^7.21.4 - checksum: bd330a2edaafeb281fbcd9357652f8d2666502567c0aad71db926e8499c773c9ea9c10dfaae30122452940326d90c8caff5c649ed8e1bf15b23f858758d3abc6 - languageName: node - linkType: hard - -"@babel/helper-module-imports@npm:^7.22.5": +"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-imports@npm:7.22.5" dependencies: @@ -1471,39 +1189,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-module-transforms@npm:7.21.5" - dependencies: - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-module-imports": ^7.21.4 - "@babel/helper-simple-access": ^7.21.5 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/helper-validator-identifier": ^7.19.1 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - checksum: 1ccfc88830675a5d485d198e918498f9683cdd46f973fdd4fe1c85b99648fb70f87fca07756c7a05dc201bd9b248c74ced06ea80c9991926ac889f53c3659675 - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.2": - version: 7.21.2 - resolution: "@babel/helper-module-transforms@npm:7.21.2" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-simple-access": ^7.20.2 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/helper-validator-identifier": ^7.19.1 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.2 - "@babel/types": ^7.21.2 - checksum: 8a1c129a4f90bdf97d8b6e7861732c9580f48f877aaaafbc376ce2482febebcb8daaa1de8bc91676d12886487603f8c62a44f9e90ee76d6cac7f9225b26a49e1 - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.22.5": +"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-transforms@npm:7.22.5" dependencies: @@ -1519,15 +1205,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: e518fe8418571405e21644cfb39cf694f30b6c47b10b006609a92469ae8b8775cbff56f0b19732343e2ea910641091c5a2dc73b56ceba04e116a33b0f8bd2fbd - languageName: node - linkType: hard - "@babel/helper-optimise-call-expression@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" @@ -1544,34 +1221,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.21.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.21.5 - resolution: "@babel/helper-plugin-utils@npm:7.21.5" - checksum: 6f086e9a84a50ea7df0d5639c8f9f68505af510ea3258b3c8ac8b175efdfb7f664436cb48996f71791a1350ba68f47ad3424131e8e718c5e2ad45564484cbb36 - languageName: node - linkType: hard - -"@babel/helper-plugin-utils@npm:^7.22.5": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.22.5 resolution: "@babel/helper-plugin-utils@npm:7.22.5" checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-wrap-function": ^7.18.9 - "@babel/types": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 4be6076192308671b046245899b703ba090dbe7ad03e0bea897bb2944ae5b88e5e85853c9d1f83f643474b54c578d8ac0800b80341a86e8538264a725fbbefec - languageName: node - linkType: hard - "@babel/helper-remap-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-remap-async-to-generator@npm:7.22.5" @@ -1586,35 +1242,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.18.6, @babel/helper-replace-supers@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-replace-supers@npm:7.21.5" - dependencies: - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-member-expression-to-functions": ^7.21.5 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - checksum: 4fd343e6f90533743d8e8a1f42e50377b3d6b27f524a27eb97ff28f075e4e55cca2383adb1b0973de358b08022aef0fec4c8d69711e1da43bf9b887b5a893677 - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/helper-replace-supers@npm:7.20.7" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-member-expression-to-functions": ^7.20.7 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.20.7 - "@babel/types": ^7.20.7 - checksum: b8e0087c9b0c1446e3c6f3f72b73b7e03559c6b570e2cfbe62c738676d9ebd8c369a708cf1a564ef88113b4330750a50232ee1131d303d478b7a5e65e46fbc7c - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.22.5": +"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-replace-supers@npm:7.22.5" dependencies: @@ -1628,24 +1256,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.20.2": - version: 7.20.2 - resolution: "@babel/helper-simple-access@npm:7.20.2" - dependencies: - "@babel/types": ^7.21.5 - checksum: ad1e96ee2e5f654ffee2369a586e5e8d2722bf2d8b028a121b4c33ebae47253f64d420157b9f0a8927aea3a9e0f18c0103e74fdd531815cf3650a0a4adca11a1 - languageName: node - linkType: hard - -"@babel/helper-simple-access@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-simple-access@npm:7.21.5" - dependencies: - "@babel/types": ^7.21.5 - checksum: ad212beaa24be3864c8c95bee02f840222457ccf5419991e2d3e3e39b0f75b77e7e857e0bf4ed428b1cd97acefc87f3831bdb0b9696d5ad0557421f398334fc3 - languageName: node - linkType: hard - "@babel/helper-simple-access@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-simple-access@npm:7.22.5" @@ -1655,16 +1265,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0": - version: 7.20.0 - resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.20.0" - dependencies: - "@babel/types": ^7.20.0 - checksum: 34da8c832d1c8a546e45d5c1d59755459ffe43629436707079989599b91e8c19e50e73af7a4bd09c95402d389266731b0d9c5f69e372d8ebd3a709c05c80d7dd - languageName: node - linkType: hard - -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0, @babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" dependencies: @@ -1673,15 +1274,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-split-export-declaration@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: c6d3dede53878f6be1d869e03e9ffbbb36f4897c7cc1527dc96c56d127d834ffe4520a6f7e467f5b6f3c2843ea0e81a7819d66ae02f707f6ac057f3d57943a2b - languageName: node - linkType: hard - "@babel/helper-split-export-declaration@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-split-export-declaration@npm:7.22.5" @@ -1691,27 +1283,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.19.4, @babel/helper-string-parser@npm:^7.22.5": +"@babel/helper-string-parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-string-parser@npm:7.22.5" checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-string-parser@npm:7.21.5" - checksum: 36c0ded452f3858e67634b81960d4bde1d1cd2a56b82f4ba2926e97864816021c885f111a7cf81de88a0ed025f49d84a393256700e9acbca2d99462d648705d8 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": - version: 7.19.1 - resolution: "@babel/helper-validator-identifier@npm:7.19.1" - checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a - languageName: node - linkType: hard - "@babel/helper-validator-identifier@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-identifier@npm:7.22.5" @@ -1719,32 +1297,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.18.6, @babel/helper-validator-option@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-validator-option@npm:7.21.0" - checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 - languageName: node - linkType: hard - -"@babel/helper-validator-option@npm:^7.22.5": +"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-option@npm:7.22.5" checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.18.9": - version: 7.19.0 - resolution: "@babel/helper-wrap-function@npm:7.19.0" - dependencies: - "@babel/helper-function-name": ^7.19.0 - "@babel/template": ^7.18.10 - "@babel/traverse": ^7.19.0 - "@babel/types": ^7.19.0 - checksum: 2453a6b134f12cc779179188c4358a66252c29b634a8195c0cf626e17f9806c3c4c40e159cd8056c2ec82b69b9056a088014fa43d6ccc1aca67da8d9605da8fd - languageName: node - linkType: hard - "@babel/helper-wrap-function@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-wrap-function@npm:7.22.5" @@ -1757,29 +1316,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helpers@npm:7.21.0" - dependencies: - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.0 - "@babel/types": ^7.21.0 - checksum: 9370dad2bb665c551869a08ac87c8bdafad53dbcdce1f5c5d498f51811456a3c005d9857562715151a0f00b2e912ac8d89f56574f837b5689f5f5072221cdf54 - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helpers@npm:7.21.5" - dependencies: - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - checksum: a6f74b8579713988e7f5adf1a986d8b5255757632ba65b2552f0f609ead5476edb784044c7e4b18f3681ee4818ca9d08c41feb9bd4e828648c25a00deaa1f9e4 - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.22.5": +"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helpers@npm:7.22.5" dependencies: @@ -1790,18 +1327,7 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/highlight@npm:7.18.6" - dependencies: - "@babel/helper-validator-identifier": ^7.18.6 - chalk: ^2.0.0 - js-tokens: ^4.0.0 - checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 - languageName: node - linkType: hard - -"@babel/highlight@npm:^7.22.5": +"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.22.5": version: 7.22.5 resolution: "@babel/highlight@npm:7.22.5" dependencies: @@ -1812,25 +1338,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/parser@npm:7.21.4" - bin: - parser: ./bin/babel-parser.js - checksum: de610ecd1bff331766d0c058023ca11a4f242bfafefc42caf926becccfb6756637d167c001987ca830dd4b34b93c629a4cef63f8c8c864a8564cdfde1989ac77 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.21.5, @babel/parser@npm:^7.21.8": - version: 7.21.8 - resolution: "@babel/parser@npm:7.21.8" - bin: - parser: ./bin/babel-parser.js - checksum: 1b9a820fedfb6ef179e6ffa1dbc080808882949dec68340a616da2aa354af66ea2886bd68e61bd444d270aa0b24ad6273e3cfaf17d6878c34bf2521becacb353 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.22.5": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/parser@npm:7.22.5" bin: @@ -1839,17 +1347,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 845bd280c55a6a91d232cfa54eaf9708ec71e594676fe705794f494bb8b711d833b752b59d1a5c154695225880c23dbc9cab0e53af16fd57807976cd3ff41b8d - languageName: node - linkType: hard - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.5" @@ -1861,19 +1358,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/plugin-proposal-optional-chaining": ^7.20.7 - peerDependencies: - "@babel/core": ^7.13.0 - checksum: d610f532210bee5342f5b44a12395ccc6d904e675a297189bc1e401cc185beec09873da523466d7fec34ae1574f7a384235cba1ccc9fe7b89ba094167897c845 - languageName: node - linkType: hard - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.5" @@ -1887,21 +1371,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 - "@babel/plugin-syntax-async-generators": ^7.8.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 - languageName: node - linkType: hard - -"@babel/plugin-proposal-class-properties@npm:^7.12.1, @babel/plugin-proposal-class-properties@npm:^7.18.6": +"@babel/plugin-proposal-class-properties@npm:^7.12.1": version: 7.18.6 resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: @@ -1913,19 +1383,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-class-static-block@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" - dependencies: - "@babel/helper-create-class-features-plugin": ^7.21.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-class-static-block": ^7.14.5 - peerDependencies: - "@babel/core": ^7.12.0 - checksum: 236c0ad089e7a7acab776cc1d355330193314bfcd62e94e78f2df35817c6144d7e0e0368976778afd6b7c13e70b5068fa84d7abbf967d4f182e60d03f9ef802b - languageName: node - linkType: hard - "@babel/plugin-proposal-decorators@npm:^7.12.12": version: 7.17.8 resolution: "@babel/plugin-proposal-decorators@npm:7.17.8" @@ -1941,18 +1398,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-dynamic-import": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f - languageName: node - linkType: hard - "@babel/plugin-proposal-export-default-from@npm:^7.12.1": version: 7.16.7 resolution: "@babel/plugin-proposal-export-default-from@npm:7.16.7" @@ -1965,42 +1410,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef - languageName: node - linkType: hard - -"@babel/plugin-proposal-json-strings@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-json-strings": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 - languageName: node - linkType: hard - -"@babel/plugin-proposal-logical-assignment-operators@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: cdd7b8136cc4db3f47714d5266f9e7b592a2ac5a94a5878787ce08890e97c8ab1ca8e94b27bfeba7b0f2b1549a026d9fc414ca2196de603df36fb32633bbdc19 - languageName: node - linkType: hard - "@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.12.1, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" @@ -2013,18 +1422,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-numeric-separator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-numeric-separator": ^7.10.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: f370ea584c55bf4040e1f78c80b4eeb1ce2e6aaa74f87d1a48266493c33931d0b6222d8cee3a082383d6bb648ab8d6b7147a06f974d3296ef3bc39c7851683ec - languageName: node - linkType: hard - "@babel/plugin-proposal-object-rest-spread@npm:7.12.1": version: 7.12.1 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.12.1" @@ -2038,7 +1435,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.12.1, @babel/plugin-proposal-object-rest-spread@npm:^7.20.7": +"@babel/plugin-proposal-object-rest-spread@npm:^7.12.1": version: 7.20.7 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" dependencies: @@ -2053,19 +1450,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7b5b39fb5d8d6d14faad6cb68ece5eeb2fd550fb66b5af7d7582402f974f5bc3684641f7c192a5a57e0f59acfae4aada6786be1eba030881ddc590666eff4d1e - languageName: node - linkType: hard - -"@babel/plugin-proposal-optional-chaining@npm:^7.12.7, @babel/plugin-proposal-optional-chaining@npm:^7.18.9, @babel/plugin-proposal-optional-chaining@npm:^7.20.7, @babel/plugin-proposal-optional-chaining@npm:^7.21.0": +"@babel/plugin-proposal-optional-chaining@npm:^7.12.7, @babel/plugin-proposal-optional-chaining@npm:^7.18.9": version: 7.21.0 resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" dependencies: @@ -2078,7 +1463,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-methods@npm:^7.12.1, @babel/plugin-proposal-private-methods@npm:^7.18.6": +"@babel/plugin-proposal-private-methods@npm:^7.12.1": version: 7.18.6 resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" dependencies: @@ -2099,7 +1484,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.12.1, @babel/plugin-proposal-private-property-in-object@npm:^7.21.0": +"@babel/plugin-proposal-private-property-in-object@npm:^7.12.1": version: 7.21.0 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0" dependencies: @@ -2113,7 +1498,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": +"@babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": version: 7.18.6 resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" dependencies: @@ -2224,17 +1609,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.20.0": - version: 7.20.0 - resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" - dependencies: - "@babel/helper-plugin-utils": ^7.19.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 6a86220e0aae40164cd3ffaf80e7c076a1be02a8f3480455dddbae05fda8140f429290027604df7a11b3f3f124866e8a6d69dbfa1dda61ee7377b920ad144d5b - languageName: node - linkType: hard - "@babel/plugin-syntax-import-assertions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-import-assertions@npm:7.22.5" @@ -2290,18 +1664,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.18.6, @babel/plugin-syntax-jsx@npm:^7.21.4, @babel/plugin-syntax-jsx@npm:^7.7.2": - version: 7.21.4 - resolution: "@babel/plugin-syntax-jsx@npm:7.21.4" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: bb7309402a1d4e155f32aa0cf216e1fa8324d6c4cfd248b03280028a015a10e46b6efd6565f515f8913918a3602b39255999c06046f7d4b8a5106be2165d724a - languageName: node - linkType: hard - -"@babel/plugin-syntax-jsx@npm:^7.22.5": +"@babel/plugin-syntax-jsx@npm:^7.22.5, @babel/plugin-syntax-jsx@npm:^7.7.2": version: 7.22.5 resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" dependencies: @@ -2400,18 +1763,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.20.0, @babel/plugin-syntax-typescript@npm:^7.7.2": - version: 7.21.4 - resolution: "@babel/plugin-syntax-typescript@npm:7.21.4" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: a59ce2477b7ae8c8945dc37dda292fef9ce46a6507b3d76b03ce7f3a6c9451a6567438b20a78ebcb3955d04095fd1ccd767075a863f79fcc30aa34dcfa441fe0 - languageName: node - linkType: hard - -"@babel/plugin-syntax-typescript@npm:^7.22.5": +"@babel/plugin-syntax-typescript@npm:^7.22.5, @babel/plugin-syntax-typescript@npm:^7.7.2": version: 7.22.5 resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" dependencies: @@ -2434,18 +1786,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: b43cabe3790c2de7710abe32df9a30005eddb2050dadd5d122c6872f679e5710e410f1b90c8f99a2aff7b614cccfecf30e7fd310236686f60d3ed43fd80b9847 - languageName: node - linkType: hard - -"@babel/plugin-transform-arrow-functions@npm:^7.22.5": +"@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-arrow-functions@npm:7.22.5" dependencies: @@ -2470,19 +1811,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" - dependencies: - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 - languageName: node - linkType: hard - "@babel/plugin-transform-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-async-to-generator@npm:7.22.5" @@ -2496,17 +1824,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0a0df61f94601e3666bf39f2cc26f5f7b22a94450fb93081edbed967bd752ce3f81d1227fefd3799f5ee2722171b5e28db61379234d1bb85b6ec689589f99d7e - languageName: node - linkType: hard - "@babel/plugin-transform-block-scoped-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" @@ -2518,18 +1835,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 15aacaadbecf96b53a750db1be4990b0d89c7f5bc3e1794b63b49fb219638c1fd25d452d15566d7e5ddf5b5f4e1a0a0055c35c1c7aee323c7b114bf49f66f4b0 - languageName: node - linkType: hard - -"@babel/plugin-transform-block-scoping@npm:^7.22.5": +"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoping@npm:7.22.5" dependencies: @@ -2565,26 +1871,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-classes@npm:7.21.0" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-compilation-targets": ^7.20.7 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-replace-supers": ^7.20.7 - "@babel/helper-split-export-declaration": ^7.18.6 - globals: ^11.1.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 088ae152074bd0e90f64659169255bfe50393e637ec8765cb2a518848b11b0299e66b91003728fd0a41563a6fdc6b8d548ece698a314fd5447f5489c22e466b7 - languageName: node - linkType: hard - -"@babel/plugin-transform-classes@npm:^7.22.5": +"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-classes@npm:7.22.5" dependencies: @@ -2603,18 +1890,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-computed-properties@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/template": ^7.20.7 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: be70e54bda8b469146459f429e5f2bd415023b87b2d5af8b10e48f465ffb02847a3ed162ca60378c004b82db848e4d62e90010d41ded7e7176b6d8d1c2911139 - languageName: node - linkType: hard - "@babel/plugin-transform-computed-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-computed-properties@npm:7.22.5" @@ -2627,18 +1902,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 43ebbe0bfa20287e34427be7c2200ce096c20913775ea75268fb47fe0e55f9510800587e6052c42fe6dffa0daaad95dd465c3e312fd1ef9785648384c45417ac - languageName: node - linkType: hard - -"@babel/plugin-transform-destructuring@npm:^7.22.5": +"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-destructuring@npm:7.22.5" dependencies: @@ -2649,19 +1913,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.4.4": - version: 7.18.6 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: cbe5d7063eb8f8cca24cd4827bc97f5641166509e58781a5f8aa47fb3d2d786ce4506a30fca2e01f61f18792783a5cb5d96bf5434c3dd1ad0de8c9cc625a53da - languageName: node - linkType: hard - -"@babel/plugin-transform-dotall-regex@npm:^7.22.5": +"@babel/plugin-transform-dotall-regex@npm:^7.22.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": version: 7.22.5 resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" dependencies: @@ -2673,17 +1925,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 220bf4a9fec5c4d4a7b1de38810350260e8ea08481bf78332a464a21256a95f0df8cd56025f346238f09b04f8e86d4158fafc9f4af57abaef31637e3b58bd4fe - languageName: node - linkType: hard - "@babel/plugin-transform-duplicate-keys@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" @@ -2707,18 +1948,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7f70222f6829c82a36005508d34ddbe6fd0974ae190683a8670dd6ff08669aaf51fef2209d7403f9bd543cb2d12b18458016c99a6ed0332ccedb3ea127b01229 - languageName: node - linkType: hard - "@babel/plugin-transform-exponentiation-operator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.22.5" @@ -2755,18 +1984,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-for-of@npm:7.21.0" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2f3f86ca1fab2929fcda6a87e4303d5c635b5f96dc9a45fd4ca083308a3020c79ac33b9543eb4640ef2b79f3586a00ab2d002a7081adb9e9d7440dce30781034 - languageName: node - linkType: hard - -"@babel/plugin-transform-for-of@npm:^7.22.5": +"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-for-of@npm:7.22.5" dependencies: @@ -2777,19 +1995,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-function-name@npm:7.18.9" - dependencies: - "@babel/helper-compilation-targets": ^7.18.9 - "@babel/helper-function-name": ^7.18.9 - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 62dd9c6cdc9714704efe15545e782ee52d74dc73916bf954b4d3bee088fb0ec9e3c8f52e751252433656c09f744b27b757fc06ed99bcde28e8a21600a1d8e597 - languageName: node - linkType: hard - "@babel/plugin-transform-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-function-name@npm:7.22.5" @@ -2815,17 +2020,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-literals@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3458dd2f1a47ac51d9d607aa18f3d321cbfa8560a985199185bed5a906bb0c61ba85575d386460bac9aed43fdd98940041fae5a67dff286f6f967707cff489f8 - languageName: node - linkType: hard - "@babel/plugin-transform-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-literals@npm:7.22.5" @@ -2849,17 +2043,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 35a3d04f6693bc6b298c05453d85ee6e41cc806538acb6928427e0e97ae06059f97d2f07d21495fcf5f70d3c13a242e2ecbd09d5c1fcb1b1a73ff528dcb0b695 - languageName: node - linkType: hard - "@babel/plugin-transform-member-expression-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.22.5" @@ -2871,18 +2054,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.20.11": - version: 7.20.11 - resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" - dependencies: - "@babel/helper-module-transforms": ^7.20.11 - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 23665c1c20c8f11c89382b588fb9651c0756d130737a7625baeaadbd3b973bc5bfba1303bedffa8fb99db1e6d848afb01016e1df2b69b18303e946890c790001 - languageName: node - linkType: hard - "@babel/plugin-transform-modules-amd@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-amd@npm:7.22.5" @@ -2895,19 +2066,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.21.2": - version: 7.21.2 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.2" - dependencies: - "@babel/helper-module-transforms": ^7.21.2 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-simple-access": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 65aa06e3e3792f39b99eb5f807034693ff0ecf80438580f7ae504f4c4448ef04147b1889ea5e6f60f3ad4a12ebbb57c6f1f979a249dadbd8d11fe22f4441918b - languageName: node - linkType: hard - "@babel/plugin-transform-modules-commonjs@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5" @@ -2921,20 +2079,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.20.11": - version: 7.20.11 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11" - dependencies: - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-module-transforms": ^7.20.11 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-validator-identifier": ^7.19.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 4546c47587f88156d66c7eb7808e903cf4bb3f6ba6ac9bc8e3af2e29e92eb9f0b3f44d52043bfd24eb25fa7827fd7b6c8bfeac0cac7584e019b87e1ecbd0e673 - languageName: node - linkType: hard - "@babel/plugin-transform-modules-systemjs@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.5" @@ -2949,18 +2093,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" - dependencies: - "@babel/helper-module-transforms": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c3b6796c6f4579f1ba5ab0cdcc73910c1e9c8e1e773c507c8bb4da33072b3ae5df73c6d68f9126dab6e99c24ea8571e1563f8710d7c421fac1cde1e434c20153 - languageName: node - linkType: hard - "@babel/plugin-transform-modules-umd@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-umd@npm:7.22.5" @@ -2973,18 +2105,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.20.5": - version: 7.20.5 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.20.5" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.20.5 - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 528c95fb1087e212f17e1c6456df041b28a83c772b9c93d2e407c9d03b72182b0d9d126770c1d6e0b23aab052599ceaf25ed6a2c0627f4249be34a83f6fae853 - languageName: node - linkType: hard - "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" @@ -2997,17 +2117,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-new-target@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: bd780e14f46af55d0ae8503b3cb81ca86dcc73ed782f177e74f498fff934754f9e9911df1f8f3bd123777eed7c1c1af4d66abab87c8daae5403e7719a6b845d1 - languageName: node - linkType: hard - "@babel/plugin-transform-new-target@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-new-target@npm:7.22.5" @@ -3058,18 +2167,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-object-super@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/helper-replace-supers": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0fcb04e15deea96ae047c21cb403607d49f06b23b4589055993365ebd7a7d7541334f06bf9642e90075e66efce6ebaf1eb0ef066fbbab802d21d714f1aac3aef - languageName: node - linkType: hard - "@babel/plugin-transform-object-super@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-object-super@npm:7.22.5" @@ -3107,18 +2204,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-parameters@npm:7.21.3" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c92128d7b1fcf54e2cab186c196bbbf55a9a6de11a83328dc2602649c9dc6d16ef73712beecd776cd49bfdc624b5f56740f4a53568d3deb9505ec666bc869da3 - languageName: node - linkType: hard - -"@babel/plugin-transform-parameters@npm:^7.22.5": +"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-parameters@npm:7.22.5" dependencies: @@ -3155,17 +2241,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 1c16e64de554703f4b547541de2edda6c01346dd3031d4d29e881aa7733785cd26d53611a4ccf5353f4d3e69097bb0111c0a93ace9e683edd94fea28c4484144 - languageName: node - linkType: hard - "@babel/plugin-transform-property-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-property-literals@npm:7.22.5" @@ -3177,17 +2252,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-display-name@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-display-name@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 51c087ab9e41ef71a29335587da28417536c6f816c292e092ffc0e0985d2f032656801d4dd502213ce32481f4ba6c69402993ffa67f0818a07606ff811e4be49 - languageName: node - linkType: hard - "@babel/plugin-transform-react-display-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-display-name@npm:7.22.5" @@ -3199,17 +2263,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-development@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-jsx-development@npm:7.18.6" - dependencies: - "@babel/plugin-transform-react-jsx": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ec9fa65db66f938b75c45e99584367779ac3e0af8afc589187262e1337c7c4205ea312877813ae4df9fb93d766627b8968d74ac2ba702e4883b1dbbe4953ecee - languageName: node - linkType: hard - "@babel/plugin-transform-react-jsx-development@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx-development@npm:7.22.5" @@ -3243,22 +2296,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.12.12, @babel/plugin-transform-react-jsx@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-jsx@npm:7.18.6" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-jsx": ^7.18.6 - "@babel/types": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 46129eaf1ab7a7a73e3e8c9d9859b630f5b381c5e19fb1559e2db7b943a7825b6715ad950623fb03fe7bd31ed618ce1d0bd539b13fa030a50c39d5a873a5ba00 - languageName: node - linkType: hard - -"@babel/plugin-transform-react-jsx@npm:^7.22.5": +"@babel/plugin-transform-react-jsx@npm:^7.12.12, @babel/plugin-transform-react-jsx@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx@npm:7.22.5" dependencies: @@ -3273,18 +2311,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-pure-annotations@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.18.6" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 97c4873d409088f437f9084d084615948198dd87fc6723ada0e7e29c5a03623c2f3e03df3f52e7e7d4d23be32a08ea00818bff302812e48713c706713bd06219 - languageName: node - linkType: hard - "@babel/plugin-transform-react-pure-annotations@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.22.5" @@ -3297,18 +2323,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.20.5": - version: 7.20.5 - resolution: "@babel/plugin-transform-regenerator@npm:7.20.5" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - regenerator-transform: ^0.15.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 13164861e71fb23d84c6270ef5330b03c54d5d661c2c7468f28e21c4f8598558ca0c8c3cb1d996219352946e849d270a61372bc93c8fbe9676e78e3ffd0dea07 - languageName: node - linkType: hard - "@babel/plugin-transform-regenerator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-regenerator@npm:7.22.5" @@ -3321,17 +2335,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0738cdc30abdae07c8ec4b233b30c31f68b3ff0eaa40eddb45ae607c066127f5fa99ddad3c0177d8e2832e3a7d3ad115775c62b431ebd6189c40a951b867a80c - languageName: node - linkType: hard - "@babel/plugin-transform-reserved-words@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" @@ -3343,18 +2346,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.12.1, @babel/plugin-transform-shorthand-properties@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: b8e4e8acc2700d1e0d7d5dbfd4fdfb935651913de6be36e6afb7e739d8f9ca539a5150075a0f9b79c88be25ddf45abb912fe7abf525f0b80f5b9d9860de685d7 - languageName: node - linkType: hard - -"@babel/plugin-transform-shorthand-properties@npm:^7.22.5": +"@babel/plugin-transform-shorthand-properties@npm:^7.12.1, @babel/plugin-transform-shorthand-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" dependencies: @@ -3365,19 +2357,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-spread@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 8ea698a12da15718aac7489d4cde10beb8a3eea1f66167d11ab1e625033641e8b328157fd1a0b55dd6531933a160c01fc2e2e61132a385cece05f26429fd0cc2 - languageName: node - linkType: hard - -"@babel/plugin-transform-spread@npm:^7.22.5": +"@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-spread@npm:7.22.5" dependencies: @@ -3389,17 +2369,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 68ea18884ae9723443ffa975eb736c8c0d751265859cd3955691253f7fee37d7a0f7efea96c8a062876af49a257a18ea0ed5fea0d95a7b3611ce40f7ee23aee3 - languageName: node - linkType: hard - "@babel/plugin-transform-sticky-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" @@ -3411,18 +2380,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3d2fcd79b7c345917f69b92a85bdc3ddd68ce2c87dc70c7d61a8373546ccd1f5cb8adc8540b49dfba08e1b82bb7b3bbe23a19efdb2b9c994db2db42906ca9fb2 - languageName: node - linkType: hard - -"@babel/plugin-transform-template-literals@npm:^7.22.5": +"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" dependencies: @@ -3433,17 +2391,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: e754e0d8b8a028c52e10c148088606e3f7a9942c57bd648fc0438e5b4868db73c386a5ed47ab6d6f0594aae29ee5ffc2ffc0f7ebee7fae560a066d6dea811cd4 - languageName: node - linkType: hard - "@babel/plugin-transform-typeof-symbol@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" @@ -3455,20 +2402,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-typescript@npm:7.21.3" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-create-class-features-plugin": ^7.21.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-typescript": ^7.20.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c16fd577bf43f633deb76fca2a8527d8ae25968c8efdf327c1955472c3e0257e62992473d1ad7f9ee95379ce2404699af405ea03346055adadd3478ad0ecd117 - languageName: node - linkType: hard - "@babel/plugin-transform-typescript@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typescript@npm:7.22.5" @@ -3483,17 +2416,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.18.10": - version: 7.21.5 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.21.5" - dependencies: - "@babel/helper-plugin-utils": ^7.21.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 6504d642d0449a275191b624bd94d3e434ae154e610bf2f0e3c109068b287d2474f68e1da64b47f21d193cd67b27ee4643877d530187670565cac46e29fd257d - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-escapes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.5" @@ -3517,18 +2439,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: d9e18d57536a2d317fb0b7c04f8f55347f3cfacb75e636b4c6fa2080ab13a3542771b5120e726b598b815891fc606d1472ac02b749c69fd527b03847f22dc25e - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" @@ -3553,92 +2463,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/preset-env@npm:7.21.4" - dependencies: - "@babel/compat-data": ^7.21.4 - "@babel/helper-compilation-targets": ^7.21.4 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-validator-option": ^7.21.0 - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.20.7 - "@babel/plugin-proposal-async-generator-functions": ^7.20.7 - "@babel/plugin-proposal-class-properties": ^7.18.6 - "@babel/plugin-proposal-class-static-block": ^7.21.0 - "@babel/plugin-proposal-dynamic-import": ^7.18.6 - "@babel/plugin-proposal-export-namespace-from": ^7.18.9 - "@babel/plugin-proposal-json-strings": ^7.18.6 - "@babel/plugin-proposal-logical-assignment-operators": ^7.20.7 - "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 - "@babel/plugin-proposal-numeric-separator": ^7.18.6 - "@babel/plugin-proposal-object-rest-spread": ^7.20.7 - "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 - "@babel/plugin-proposal-optional-chaining": ^7.21.0 - "@babel/plugin-proposal-private-methods": ^7.18.6 - "@babel/plugin-proposal-private-property-in-object": ^7.21.0 - "@babel/plugin-proposal-unicode-property-regex": ^7.18.6 - "@babel/plugin-syntax-async-generators": ^7.8.4 - "@babel/plugin-syntax-class-properties": ^7.12.13 - "@babel/plugin-syntax-class-static-block": ^7.14.5 - "@babel/plugin-syntax-dynamic-import": ^7.8.3 - "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - "@babel/plugin-syntax-import-assertions": ^7.20.0 - "@babel/plugin-syntax-json-strings": ^7.8.3 - "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 - "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 - "@babel/plugin-syntax-numeric-separator": ^7.10.4 - "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 - "@babel/plugin-syntax-optional-chaining": ^7.8.3 - "@babel/plugin-syntax-private-property-in-object": ^7.14.5 - "@babel/plugin-syntax-top-level-await": ^7.14.5 - "@babel/plugin-transform-arrow-functions": ^7.20.7 - "@babel/plugin-transform-async-to-generator": ^7.20.7 - "@babel/plugin-transform-block-scoped-functions": ^7.18.6 - "@babel/plugin-transform-block-scoping": ^7.21.0 - "@babel/plugin-transform-classes": ^7.21.0 - "@babel/plugin-transform-computed-properties": ^7.20.7 - "@babel/plugin-transform-destructuring": ^7.21.3 - "@babel/plugin-transform-dotall-regex": ^7.18.6 - "@babel/plugin-transform-duplicate-keys": ^7.18.9 - "@babel/plugin-transform-exponentiation-operator": ^7.18.6 - "@babel/plugin-transform-for-of": ^7.21.0 - "@babel/plugin-transform-function-name": ^7.18.9 - "@babel/plugin-transform-literals": ^7.18.9 - "@babel/plugin-transform-member-expression-literals": ^7.18.6 - "@babel/plugin-transform-modules-amd": ^7.20.11 - "@babel/plugin-transform-modules-commonjs": ^7.21.2 - "@babel/plugin-transform-modules-systemjs": ^7.20.11 - "@babel/plugin-transform-modules-umd": ^7.18.6 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.20.5 - "@babel/plugin-transform-new-target": ^7.18.6 - "@babel/plugin-transform-object-super": ^7.18.6 - "@babel/plugin-transform-parameters": ^7.21.3 - "@babel/plugin-transform-property-literals": ^7.18.6 - "@babel/plugin-transform-regenerator": ^7.20.5 - "@babel/plugin-transform-reserved-words": ^7.18.6 - "@babel/plugin-transform-shorthand-properties": ^7.18.6 - "@babel/plugin-transform-spread": ^7.20.7 - "@babel/plugin-transform-sticky-regex": ^7.18.6 - "@babel/plugin-transform-template-literals": ^7.18.9 - "@babel/plugin-transform-typeof-symbol": ^7.18.9 - "@babel/plugin-transform-unicode-escapes": ^7.18.10 - "@babel/plugin-transform-unicode-regex": ^7.18.6 - "@babel/preset-modules": ^0.1.5 - "@babel/types": ^7.21.4 - babel-plugin-polyfill-corejs2: ^0.3.3 - babel-plugin-polyfill-corejs3: ^0.6.0 - babel-plugin-polyfill-regenerator: ^0.4.1 - core-js-compat: ^3.25.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 1e328674c4b39e985fa81e5a8eee9aaab353dea4ff1f28f454c5e27a6498c762e25d42e827f5bfc9d7acf6c9b8bc317b5283aa7c83d9fd03c1a89e5c08f334f9 - languageName: node - linkType: hard - -"@babel/preset-env@npm:^7.20.2, @babel/preset-env@npm:~7.22.5": +"@babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.20.2, @babel/preset-env@npm:^7.21.4, @babel/preset-env@npm:~7.22.5": version: 7.22.5 resolution: "@babel/preset-env@npm:7.22.5" dependencies: @@ -3756,23 +2581,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.12.10": - version: 7.18.6 - resolution: "@babel/preset-react@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/helper-validator-option": ^7.18.6 - "@babel/plugin-transform-react-display-name": ^7.18.6 - "@babel/plugin-transform-react-jsx": ^7.18.6 - "@babel/plugin-transform-react-jsx-development": ^7.18.6 - "@babel/plugin-transform-react-pure-annotations": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 540d9cf0a0cc0bb07e6879994e6fb7152f87dafbac880b56b65e2f528134c7ba33e0cd140b58700c77b2ebf4c81fa6468fed0ba391462d75efc7f8c1699bb4c3 - languageName: node - linkType: hard - -"@babel/preset-react@npm:^7.18.6": +"@babel/preset-react@npm:^7.12.10, @babel/preset-react@npm:^7.18.6": version: 7.22.5 resolution: "@babel/preset-react@npm:7.22.5" dependencies: @@ -3788,22 +2597,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.12.7, @babel/preset-typescript@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/preset-typescript@npm:7.21.4" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-validator-option": ^7.21.0 - "@babel/plugin-syntax-jsx": ^7.21.4 - "@babel/plugin-transform-modules-commonjs": ^7.21.2 - "@babel/plugin-transform-typescript": ^7.21.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 83b2f2bf7be3a970acd212177525f58bbb1f2e042b675a47d021a675ae27cf00b6b6babfaf3ae5c980592c9ed1b0712e5197796b691905d25c99f9006478ea06 - languageName: node - linkType: hard - -"@babel/preset-typescript@npm:~7.22.5": +"@babel/preset-typescript@npm:^7.12.7, @babel/preset-typescript@npm:^7.21.4, @babel/preset-typescript@npm:~7.22.5": version: 7.22.5 resolution: "@babel/preset-typescript@npm:7.22.5" dependencies: @@ -3818,22 +2612,7 @@ __metadata: languageName: node linkType: hard -"@babel/register@npm:^7.12.1": - version: 7.18.9 - resolution: "@babel/register@npm:7.18.9" - dependencies: - clone-deep: ^4.0.1 - find-cache-dir: ^2.0.0 - make-dir: ^2.1.0 - pirates: ^4.0.5 - source-map-support: ^0.5.16 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 4aeaff97e061a397f632659082ba86c539ef8194697b236d991c10d1c2ea8f73213d3b5b3b2c24625951a1ef726b7a7d2e70f70ffcb37f79ef0c1a745eebef21 - languageName: node - linkType: hard - -"@babel/register@npm:^7.18.9": +"@babel/register@npm:^7.12.1, @babel/register@npm:^7.18.9": version: 7.22.5 resolution: "@babel/register@npm:7.22.5" dependencies: @@ -3864,25 +2643,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": - version: 7.21.0 - resolution: "@babel/runtime@npm:7.21.0" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.20.1": - version: 7.21.5 - resolution: "@babel/runtime@npm:7.21.5" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 358f2779d3187f5c67ad302e8f8d435412925d0b991d133c7d4a7b1ddd5a3fda1b6f34537cb64628dfd96a27ae46df105bed3895b8d754b88cacdded8d1129dd - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.20.13, @babel/runtime@npm:~7.22.5": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2, @babel/runtime@npm:~7.22.5": version: 7.22.5 resolution: "@babel/runtime@npm:7.22.5" dependencies: @@ -3891,15 +2652,6 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.20.6": - version: 7.20.7 - resolution: "@babel/runtime@npm:7.20.7" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 4629ce5c46f06cca9cfb9b7fc00d48003335a809888e2b91ec2069a2dcfbfef738480cff32ba81e0b7c290f8918e5c22ddcf2b710001464ee84ba62c7e32a3a3 - languageName: node - linkType: hard - "@babel/runtime@npm:~7.5.4": version: 7.5.5 resolution: "@babel/runtime@npm:7.5.5" @@ -3909,18 +2661,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.12.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.3.3": - version: 7.20.7 - resolution: "@babel/template@npm:7.20.7" - dependencies: - "@babel/code-frame": ^7.18.6 - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 - checksum: 2eb1a0ab8d415078776bceb3473d07ab746e6bb4c2f6ca46ee70efb284d75c4a32bb0cd6f4f4946dec9711f9c0780e8e5d64b743208deac6f8e9858afadc349e - languageName: node - linkType: hard - -"@babel/template@npm:^7.22.5": +"@babel/template@npm:^7.12.7, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" dependencies: @@ -3931,43 +2672,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.19.0, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4, @babel/traverse@npm:^7.7.2": - version: 7.21.4 - resolution: "@babel/traverse@npm:7.21.4" - dependencies: - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.4 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.21.4 - "@babel/types": ^7.21.4 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: f22f067c2d9b6497abf3d4e53ea71f3aa82a21f2ed434dd69b8c5767f11f2a4c24c8d2f517d2312c9e5248e5c69395fdca1c95a2b3286122c75f5783ddb6f53c - languageName: node - linkType: hard - -"@babel/traverse@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/traverse@npm:7.21.5" - dependencies: - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.5 - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.21.5 - "@babel/types": ^7.21.5 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: b403733fa7d858f0c8e224f0434a6ade641bc469a4f92975363391e796629d5bf53e544761dfe85039aab92d5389ebe7721edb309d7a5bb7df2bf74f37bf9f47 - languageName: node - linkType: hard - -"@babel/traverse@npm:^7.22.5": +"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.22.5, @babel/traverse@npm:^7.7.2": version: 7.22.5 resolution: "@babel/traverse@npm:7.22.5" dependencies: @@ -3985,29 +2690,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.19.0, @babel/types@npm:^7.2.0, @babel/types@npm:^7.21.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.21.5 - resolution: "@babel/types@npm:7.21.5" - dependencies: - "@babel/helper-string-parser": ^7.21.5 - "@babel/helper-validator-identifier": ^7.19.1 - to-fast-properties: ^2.0.0 - checksum: 43242a99c612d13285ee4af46cc0f1066bcb6ffd38307daef7a76e8c70f36cfc3255eb9e75c8e768b40e761176c313aec4d5c0b9d97a21e494d49d5fd123a9f7 - languageName: node - linkType: hard - -"@babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/types@npm:7.21.4" - dependencies: - "@babel/helper-string-parser": ^7.19.4 - "@babel/helper-validator-identifier": ^7.19.1 - to-fast-properties: ^2.0.0 - checksum: 587bc55a91ce003b0f8aa10d70070f8006560d7dc0360dc0406d306a2cb2a10154e2f9080b9c37abec76907a90b330a536406cb75e6bdc905484f37b75c73219 - languageName: node - linkType: hard - -"@babel/types@npm:^7.22.5": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.22.5 resolution: "@babel/types@npm:7.22.5" dependencies: @@ -4642,13 +3325,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/android-arm64@npm:0.17.18" - checksum: ec47777acf96ffe5e36426e5c5715f74e154ddd2a4b2fcd12748250d7b3ded51c5a1a8a5f896f1524e52d3abf4b302aad0b2f30ac23b4efc41de2d01e359a34a - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm64@npm:0.17.19" @@ -4656,13 +3332,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/android-arm@npm:0.17.18" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - "@esbuild/android-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm@npm:0.17.19" @@ -4670,13 +3339,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/android-x64@npm:0.17.18" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - "@esbuild/android-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-x64@npm:0.17.19" @@ -4684,13 +3346,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/darwin-arm64@npm:0.17.18" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/darwin-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-arm64@npm:0.17.19" @@ -4698,13 +3353,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/darwin-x64@npm:0.17.18" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - "@esbuild/darwin-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-x64@npm:0.17.19" @@ -4712,13 +3360,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/freebsd-arm64@npm:0.17.18" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/freebsd-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-arm64@npm:0.17.19" @@ -4726,13 +3367,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/freebsd-x64@npm:0.17.18" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/freebsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-x64@npm:0.17.19" @@ -4740,13 +3374,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-arm64@npm:0.17.18" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/linux-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm64@npm:0.17.19" @@ -4754,13 +3381,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-arm@npm:0.17.18" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - "@esbuild/linux-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm@npm:0.17.19" @@ -4768,13 +3388,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-ia32@npm:0.17.18" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/linux-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ia32@npm:0.17.19" @@ -4782,13 +3395,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-loong64@npm:0.17.18" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - "@esbuild/linux-loong64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-loong64@npm:0.17.19" @@ -4796,13 +3402,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-mips64el@npm:0.17.18" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - "@esbuild/linux-mips64el@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-mips64el@npm:0.17.19" @@ -4810,13 +3409,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-ppc64@npm:0.17.18" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/linux-ppc64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ppc64@npm:0.17.19" @@ -4824,13 +3416,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-riscv64@npm:0.17.18" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - "@esbuild/linux-riscv64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-riscv64@npm:0.17.19" @@ -4838,13 +3423,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-s390x@npm:0.17.18" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - "@esbuild/linux-s390x@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-s390x@npm:0.17.19" @@ -4852,13 +3430,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-x64@npm:0.17.18" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - "@esbuild/linux-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-x64@npm:0.17.19" @@ -4866,13 +3437,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/netbsd-x64@npm:0.17.18" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/netbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/netbsd-x64@npm:0.17.19" @@ -4880,13 +3444,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/openbsd-x64@npm:0.17.18" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/openbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/openbsd-x64@npm:0.17.19" @@ -4894,13 +3451,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/sunos-x64@npm:0.17.18" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - "@esbuild/sunos-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/sunos-x64@npm:0.17.19" @@ -4908,13 +3458,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/win32-arm64@npm:0.17.18" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/win32-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-arm64@npm:0.17.19" @@ -4922,13 +3465,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/win32-ia32@npm:0.17.18" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/win32-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-ia32@npm:0.17.19" @@ -4936,13 +3472,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/win32-x64@npm:0.17.18" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@esbuild/win32-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-x64@npm:0.17.19" @@ -5166,15 +3695,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/date@npm:^3.0.1": - version: 3.0.1 - resolution: "@internationalized/date@npm:3.0.1" - dependencies: - "@babel/runtime": ^7.6.2 - checksum: ff51a00550322a5df3d3051e8ffdf3d7741851149e8ba300883e01402249602e87cc50b27b972753d9af88c5374df83c24adf58cae5e269100cb946a3b12cd56 - languageName: node - linkType: hard - "@internationalized/date@npm:^3.2.0": version: 3.2.0 resolution: "@internationalized/date@npm:3.2.0" @@ -5194,16 +3714,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/message@npm:^3.0.9": - version: 3.0.9 - resolution: "@internationalized/message@npm:3.0.9" - dependencies: - "@babel/runtime": ^7.6.2 - intl-messageformat: ^10.1.0 - checksum: b3f7f5a8e1d8df99efb3463ca07edb976ecf95d28de19a47d92fb19c093052b1a092aeaa226dc69d07143854bdbeb8519a0ac8ba8c900c4b0f565151d735ca7f - languageName: node - linkType: hard - "@internationalized/message@npm:^3.1.0": version: 3.1.0 resolution: "@internationalized/message@npm:3.1.0" @@ -5223,15 +3733,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/number@npm:^3.1.1": - version: 3.1.1 - resolution: "@internationalized/number@npm:3.1.1" - dependencies: - "@babel/runtime": ^7.6.2 - checksum: 9979ea1ca7388de75193c9d36f19d928fbcb715d456d153c30cafadd2ce1ceae011f55c966d424f4561ec04de14d3b48b8fe16a9e2737273829a813c4f7203a3 - languageName: node - linkType: hard - "@internationalized/number@npm:^3.2.0": version: 3.2.0 resolution: "@internationalized/number@npm:3.2.0" @@ -5250,15 +3751,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/string@npm:^3.0.0": - version: 3.0.0 - resolution: "@internationalized/string@npm:3.0.0" - dependencies: - "@babel/runtime": ^7.6.2 - checksum: fc347cf80cd4ee009d1c467dca2c6908a919ad152086bf5e8c1a0aede0383fb317695fc5d82abe033ec90ad62108297130b653b63b9529f2e032999798ae4a81 - languageName: node - linkType: hard - "@internationalized/string@npm:^3.1.0": version: 3.1.0 resolution: "@internationalized/string@npm:3.1.0" @@ -5596,20 +4088,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0": +"@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": version: 3.1.0 resolution: "@jridgewell/resolve-uri@npm:3.1.0" checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.0.5 - resolution: "@jridgewell/resolve-uri@npm:3.0.5" - checksum: 1ee652b693da7979ac4007926cc3f0a32b657ffeb913e111f44e5b67153d94a2f28a1d560101cc0cf8087625468293a69a00f634a2914e1a6d0817ba2039a913 - languageName: node - linkType: hard - "@jridgewell/set-array@npm:^1.0.1": version: 1.1.2 resolution: "@jridgewell/set-array@npm:1.1.2" @@ -5617,16 +4102,6 @@ __metadata: languageName: node linkType: hard -"@jridgewell/source-map@npm:^0.3.2": - version: 0.3.2 - resolution: "@jridgewell/source-map@npm:0.3.2" - dependencies: - "@jridgewell/gen-mapping": ^0.3.0 - "@jridgewell/trace-mapping": ^0.3.9 - checksum: 1b83f0eb944e77b70559a394d5d3b3f98a81fcc186946aceb3ef42d036762b52ef71493c6c0a3b7c1d2f08785f53ba2df1277fe629a06e6109588ff4cdcf7482 - languageName: node - linkType: hard - "@jridgewell/source-map@npm:^0.3.3": version: 0.3.3 resolution: "@jridgewell/source-map@npm:0.3.3" @@ -5637,20 +4112,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14": +"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10": version: 1.4.14 resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.11 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.11" - checksum: 3b2afaf8400fb07a36db60e901fcce6a746cdec587310ee9035939d89878e57b2dec8173b0b8f63176f647efa352294049a53c49739098eb907ff81fec2547c8 - languageName: node - linkType: hard - "@jridgewell/trace-mapping@npm:0.3.9": version: 0.3.9 resolution: "@jridgewell/trace-mapping@npm:0.3.9" @@ -5661,7 +4129,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.14, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.18 resolution: "@jridgewell/trace-mapping@npm:0.3.18" dependencies: @@ -5709,16 +4177,7 @@ __metadata: languageName: node linkType: hard -"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3": - version: 1.1.5 - resolution: "@lezer/highlight@npm:1.1.5" - dependencies: - "@lezer/common": ^1.0.0 - checksum: 1f0b0a3dc7e1f23d889ce7a61d9ce1ba4d3b307205baf58f97252588df4c6751e4c86d39c20cd0bc7ac39ab82ff78a9251db91148b3b069965ec55d5fe9c4ef5 - languageName: node - linkType: hard - -"@lezer/highlight@npm:^1.1.6": +"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3, @lezer/highlight@npm:^1.1.6": version: 1.1.6 resolution: "@lezer/highlight@npm:1.1.6" dependencies: @@ -6874,24 +5333,6 @@ __metadata: languageName: node linkType: hard -"@react-aria/i18n@npm:^3.6.0": - version: 3.6.0 - resolution: "@react-aria/i18n@npm:3.6.0" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/date": ^3.0.1 - "@internationalized/message": ^3.0.9 - "@internationalized/number": ^3.1.1 - "@internationalized/string": ^3.0.0 - "@react-aria/ssr": ^3.3.0 - "@react-aria/utils": ^3.13.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: ede9cd611e15fe2975556dfe695bdcb67cbcb8d2dfff7677174f86f1418421491fbbbfd8eab40e724a8db24877d2f980df6e50d26d29d5b3e607ca39b42befc3 - languageName: node - linkType: hard - "@react-aria/i18n@npm:^3.7.0, @react-aria/i18n@npm:^3.7.1": version: 3.7.1 resolution: "@react-aria/i18n@npm:3.7.1" @@ -7297,17 +5738,6 @@ __metadata: languageName: node linkType: hard -"@react-aria/ssr@npm:^3.3.0": - version: 3.3.0 - resolution: "@react-aria/ssr@npm:3.3.0" - dependencies: - "@babel/runtime": ^7.6.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 0b7677ef521c65452460601dce3c264b67baa75ef7c99e9755ea55913765054156b6157c9c42e3d56aba86d1704b8b2aeb7672e4084f2f375fe1ec481e33c8c6 - languageName: node - linkType: hard - "@react-aria/ssr@npm:^3.5.0, @react-aria/ssr@npm:^3.6.0": version: 3.6.0 resolution: "@react-aria/ssr@npm:3.6.0" @@ -7462,21 +5892,6 @@ __metadata: languageName: node linkType: hard -"@react-aria/utils@npm:^3.13.3": - version: 3.13.3 - resolution: "@react-aria/utils@npm:3.13.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-aria/ssr": ^3.3.0 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - clsx: ^1.1.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: b6d87ddb8e1d93b00405473099390c854647d81c0419de53cc4a7f02bdcca6d030776fba9f4b241400af13082bafc820dd5ce05c168e8f5a2c43a1b2660fb2ad - languageName: node - linkType: hard - "@react-aria/utils@npm:^3.15.0, @react-aria/utils@npm:^3.16.0": version: 3.16.0 resolution: "@react-aria/utils@npm:3.16.0" @@ -7755,23 +6170,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/calendar@npm:^3.0.2": - version: 3.0.2 - resolution: "@react-stately/calendar@npm:3.0.2" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/date": ^3.0.1 - "@react-stately/utils": ^3.5.1 - "@react-types/calendar": ^3.0.2 - "@react-types/datepicker": ^3.1.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: c093cab8761b1e16603abcde63f78dfefdb7fdf4cc269e41602ab3a7c93f9391d29ac68cc66e030c553305af7d96ff9afa3795123211a59316819937a8181956 - languageName: node - linkType: hard - -"@react-stately/calendar@npm:^3.2.0": +"@react-stately/calendar@npm:^3.0.2, @react-stately/calendar@npm:^3.2.0": version: 3.2.0 resolution: "@react-stately/calendar@npm:3.2.0" dependencies: @@ -7787,21 +6186,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/checkbox@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/checkbox@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/toggle": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/checkbox": ^3.3.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 9035b595fa21cc1bef7e04249ec9df2293e93310dd644e4d32087ce19bd77aae38db3e676f6fdffbde875bc9a318f05dd60c61ab6e0d9b524222438e7ef31cd7 - languageName: node - linkType: hard - -"@react-stately/checkbox@npm:^3.4.1": +"@react-stately/checkbox@npm:^3.2.1, @react-stately/checkbox@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/checkbox@npm:3.4.1" dependencies: @@ -7816,19 +6201,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/collections@npm:^3.4.3": - version: 3.4.3 - resolution: "@react-stately/collections@npm:3.4.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f9045cdac0b20f7d7464ac37c0402511f7c5a727676d0cfefef74a553247d0dd1c816ea5804aac318d85ea5708599f9c9c2e8bd37165b5c6eec100e27f3832b9 - languageName: node - linkType: hard - -"@react-stately/collections@npm:^3.7.0": +"@react-stately/collections@npm:^3.4.3, @react-stately/collections@npm:^3.7.0": version: 3.7.0 resolution: "@react-stately/collections@npm:3.7.0" dependencies: @@ -7858,24 +6231,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/combobox@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/combobox@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/list": ^3.5.3 - "@react-stately/menu": ^3.4.1 - "@react-stately/select": ^3.3.1 - "@react-stately/utils": ^3.5.1 - "@react-types/combobox": ^3.5.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3e9a9050e8e20c96ae703876e652d28d2e3cf9dca79008d8e0f9fd096e88f74215add97e7d4aec9fe93afd64ebd676e5593d5178a28ad76c180207740fc47712 - languageName: node - linkType: hard - -"@react-stately/combobox@npm:^3.5.0": +"@react-stately/combobox@npm:^3.2.1, @react-stately/combobox@npm:^3.5.0": version: 3.5.0 resolution: "@react-stately/combobox@npm:3.5.0" dependencies: @@ -7905,24 +6261,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/datepicker@npm:^3.0.2": - version: 3.0.2 - resolution: "@react-stately/datepicker@npm:3.0.2" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/date": ^3.0.1 - "@internationalized/string": ^3.0.0 - "@react-stately/overlays": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/datepicker": ^3.1.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: d0250033d8f4625442177eac1ced6fe446877df9607bd1d7bdea11daae47166072304ee66d4ce1fe12886ef24c0cc1983ac5807a1fe07b05a5749d6b8302f47b - languageName: node - linkType: hard - -"@react-stately/datepicker@npm:^3.4.0": +"@react-stately/datepicker@npm:^3.0.2, @react-stately/datepicker@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/datepicker@npm:3.4.0" dependencies: @@ -7952,20 +6291,6 @@ __metadata: languageName: node linkType: hard -"@react-stately/grid@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-stately/grid@npm:3.3.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/selection": ^3.10.3 - "@react-types/grid": ^3.1.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 84e1f24d2dcac51b1ab99f0ad403c965eb9988fa236054a5c137efb1917a455d56a1b78f820a77c3af38895d60a24884cfeac5a482b36390b629612ee8c7e7f3 - languageName: node - linkType: hard - "@react-stately/grid@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/grid@npm:3.6.0" @@ -7998,22 +6323,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/list@npm:^3.5.3": - version: 3.5.3 - resolution: "@react-stately/list@npm:3.5.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/selection": ^3.10.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 162ba719db06a1649bbeb655c78e8a3f3c17a4c02f3318479ce2cc71940052f4a3cc98e67fd604f48ed89f199c731fb6d7c4d6e7b36d53593a0fc9b38d5e465c - languageName: node - linkType: hard - -"@react-stately/list@npm:^3.8.0": +"@react-stately/list@npm:^3.5.3, @react-stately/list@npm:^3.8.0": version: 3.8.0 resolution: "@react-stately/list@npm:3.8.0" dependencies: @@ -8028,22 +6338,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/menu@npm:^3.4.1": - version: 3.4.1 - resolution: "@react-stately/menu@npm:3.4.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/overlays": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/menu": ^3.7.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: a944d6e3a3caf400ffc52738ee8d586db6c6846d0ecc009de4bbedc88202f63d6bbddbd3d577f730f98f28404b077676af4c307f4ba09314c79cf56087a5aa8c - languageName: node - linkType: hard - -"@react-stately/menu@npm:^3.5.1": +"@react-stately/menu@npm:^3.4.1, @react-stately/menu@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/menu@npm:3.5.1" dependencies: @@ -8058,22 +6353,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/numberfield@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/numberfield@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/number": ^3.1.1 - "@react-stately/utils": ^3.5.1 - "@react-types/numberfield": ^3.3.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 5698d237c8fbe65cc7ab85c586ffadd92d085f15cab542003419deeccc2f13f2aa839dc844df8853648d892d6580fd4dd15a0b0d4eba86a467afbdb8d3c1675f - languageName: node - linkType: hard - -"@react-stately/numberfield@npm:^3.4.1": +"@react-stately/numberfield@npm:^3.2.1, @react-stately/numberfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/numberfield@npm:3.4.1" dependencies: @@ -8088,20 +6368,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/overlays@npm:^3.4.1": - version: 3.4.1 - resolution: "@react-stately/overlays@npm:3.4.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/overlays": ^3.6.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3e0e8711c55198b75cb23a682530969c997fdd21c280a9a1356327ff3806252a70ef13e4efc7734902edfd58d6c2cc9d2624a37d8394ad44e9d33b09186510e3 - languageName: node - linkType: hard - -"@react-stately/overlays@npm:^3.5.1": +"@react-stately/overlays@npm:^3.4.1, @react-stately/overlays@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/overlays@npm:3.5.1" dependencies: @@ -8128,20 +6395,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/radio@npm:^3.5.1": - version: 3.5.1 - resolution: "@react-stately/radio@npm:3.5.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/radio": ^3.2.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 7a60de8afb5d8ccaf33da66613ae55a4b2eca75bacae902574282c33ab66684b1ae5db95b2743fdcc926d1c0464af7e6d837f6a5b85bb00836a9c78ba65c3623 - languageName: node - linkType: hard - -"@react-stately/radio@npm:^3.8.0": +"@react-stately/radio@npm:^3.5.1, @react-stately/radio@npm:^3.8.0": version: 3.8.0 resolution: "@react-stately/radio@npm:3.8.0" dependencies: @@ -8155,21 +6409,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/searchfield@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-stately/searchfield@npm:3.3.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/searchfield": ^3.3.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f52776a294450382ea9f2abacea6b2972ea4f96ef6ffaff33c62f783881ceb74cd6aec959178499d6c7acf49b3a671d1902f0eb9cc4f2dba486ca88f7514693b - languageName: node - linkType: hard - -"@react-stately/searchfield@npm:^3.4.1": +"@react-stately/searchfield@npm:^3.3.1, @react-stately/searchfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/searchfield@npm:3.4.1" dependencies: @@ -8183,25 +6423,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/select@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-stately/select@npm:3.3.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/list": ^3.5.3 - "@react-stately/menu": ^3.4.1 - "@react-stately/selection": ^3.10.3 - "@react-stately/utils": ^3.5.1 - "@react-types/select": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 0701cadd640fdea8a3a1c7048e459f701fc8ec9c0ef1fb9692fd70faa5bb7ce23475aba988f57dff90a3db71cbbf8b1ba49edc3df43e744550fbd0e2dcc3575f - languageName: node - linkType: hard - -"@react-stately/select@npm:^3.5.0": +"@react-stately/select@npm:^3.3.1, @react-stately/select@npm:^3.5.0": version: 3.5.0 resolution: "@react-stately/select@npm:3.5.0" dependencies: @@ -8219,21 +6441,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/selection@npm:^3.10.3": - version: 3.10.3 - resolution: "@react-stately/selection@npm:3.10.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f65af198fa9199bc6bcf76279e2131b605e3ce449cc61d404de34993c81f499d0aba34916e8e8fd867d01ae60786ea3c3b725f3c73153674812bf29e64c6a531 - languageName: node - linkType: hard - -"@react-stately/selection@npm:^3.13.0": +"@react-stately/selection@npm:^3.10.3, @react-stately/selection@npm:^3.13.0": version: 3.13.0 resolution: "@react-stately/selection@npm:3.13.0" dependencies: @@ -8263,23 +6471,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/slider@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/slider@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-aria/i18n": ^3.6.0 - "@react-aria/utils": ^3.13.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - "@react-types/slider": ^3.2.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3d20eae41b79e481fc45cb4671b17ea20010f199790c963766a58067df18c1b83b41b1394ff3b053b32306cd952bad12331dec09c2a6a6c0c060f336aafee0ca - languageName: node - linkType: hard - -"@react-stately/slider@npm:^3.3.1": +"@react-stately/slider@npm:^3.2.1, @react-stately/slider@npm:^3.3.1": version: 3.3.1 resolution: "@react-stately/slider@npm:3.3.1" dependencies: @@ -8295,24 +6487,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/table@npm:^3.4.0": - version: 3.4.0 - resolution: "@react-stately/table@npm:3.4.0" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/grid": ^3.3.1 - "@react-stately/selection": ^3.10.3 - "@react-types/grid": ^3.1.3 - "@react-types/shared": ^3.14.1 - "@react-types/table": ^3.3.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f3571875fe9978d1f99554d8a31b3af3ced6ac84fde77ed175620f5ce76952833b98ee41b383f5098489c41f504942cedcffe604a4ec6158bbe320267eb70d01 - languageName: node - linkType: hard - -"@react-stately/table@npm:^3.9.0": +"@react-stately/table@npm:^3.4.0, @react-stately/table@npm:^3.9.0": version: 3.9.0 resolution: "@react-stately/table@npm:3.9.0" dependencies: @@ -8329,21 +6504,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/tabs@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/tabs@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/list": ^3.5.3 - "@react-stately/utils": ^3.5.1 - "@react-types/tabs": ^3.1.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 593d4ea004ed89156ebf6e2eea401d30e4b06e9eae0f83752550bc5d3d776008577ba0e6baca9791c2e1c0af0f15881a8f95f18923132af08de172cecf097d20 - languageName: node - linkType: hard - -"@react-stately/tabs@npm:^3.4.0": +"@react-stately/tabs@npm:^3.2.1, @react-stately/tabs@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/tabs@npm:3.4.0" dependencies: @@ -8358,21 +6519,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/toggle@npm:^3.4.1": - version: 3.4.1 - resolution: "@react-stately/toggle@npm:3.4.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/checkbox": ^3.3.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 6cc297ac5c840aa20a6d304947a4d869b857c9dc522b7e77cf798f1815ebd5e5ae1f00aeb812fa452fbbfada1069e814b9e1aaf2751b747f875f8b88d88c21fe - languageName: node - linkType: hard - -"@react-stately/toggle@npm:^3.5.1": +"@react-stately/toggle@npm:^3.4.1, @react-stately/toggle@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/toggle@npm:3.5.1" dependencies: @@ -8386,21 +6533,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/tooltip@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/tooltip@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/overlays": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/tooltip": ^3.2.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: dbb650986c11284dc45b6c0940e3a5aecb7d5e1af92828ae93b4ec1441b580461340033f427523b16f216afb815ebdc491f7aae361e5cd3bcc3dcea1268c76ab - languageName: node - linkType: hard - -"@react-stately/tooltip@npm:^3.4.0": +"@react-stately/tooltip@npm:^3.2.1, @react-stately/tooltip@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/tooltip@npm:3.4.0" dependencies: @@ -8414,22 +6547,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/tree@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-stately/tree@npm:3.3.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/selection": ^3.10.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 4e1a94cb478124a2443e84dbf0160dd3a5298e79478336f07003b8c5fcdb26043c65a94439a17315cf00e7f66bf6fd5e3e6fbcb44bced3352554d8f7be94899a - languageName: node - linkType: hard - -"@react-stately/tree@npm:^3.6.0": +"@react-stately/tree@npm:^3.3.3, @react-stately/tree@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/tree@npm:3.6.0" dependencies: @@ -8455,17 +6573,6 @@ __metadata: languageName: node linkType: hard -"@react-stately/utils@npm:^3.5.1": - version: 3.5.1 - resolution: "@react-stately/utils@npm:3.5.1" - dependencies: - "@babel/runtime": ^7.6.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f748331ae393f97b3e6fcccd37b767358f49229520b9500f82ed4c620bff36ef3c01d4ba9679ac7b9d6d78c5f6e711186c98bd0e6482ec27a6fbf26c5d0aa3cc - languageName: node - linkType: hard - "@react-stately/utils@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/utils@npm:3.6.0" @@ -8524,18 +6631,6 @@ __metadata: languageName: node linkType: hard -"@react-types/calendar@npm:^3.0.2": - version: 3.0.2 - resolution: "@react-types/calendar@npm:3.0.2" - dependencies: - "@internationalized/date": ^3.0.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: a3fd271d85064837c3b7a495e4048c25da1bbbc21015cfadd970f9959e8c802c9152e25ea772ffd815655e392ce07ce75c688726e70bb4cf6959605bc8257c8e - languageName: node - linkType: hard - "@react-types/calendar@npm:^3.2.0": version: 3.2.0 resolution: "@react-types/calendar@npm:3.2.0" @@ -8548,17 +6643,6 @@ __metadata: languageName: node linkType: hard -"@react-types/checkbox@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-types/checkbox@npm:3.3.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: d1da491ff3bf14f894dbeab5ace3a397ead306d2cc4a820d2a653e038a5628495417feb10a4e07c05dcfce208ae9303c35de7e57d1b21a6b59ca1acca11b80d8 - languageName: node - linkType: hard - "@react-types/checkbox@npm:^3.4.3": version: 3.4.3 resolution: "@react-types/checkbox@npm:3.4.3" @@ -8582,17 +6666,6 @@ __metadata: languageName: node linkType: hard -"@react-types/combobox@npm:^3.5.3": - version: 3.5.3 - resolution: "@react-types/combobox@npm:3.5.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 41e1371f1efa48fe4d56afffeca59d1ed9dad75565c3d67fdf9f6c594529113ce9a8053b95d419682364878a6df0fd6a7178c20e6735778eea2abe74de1ca24f - languageName: node - linkType: hard - "@react-types/combobox@npm:^3.6.1": version: 3.6.1 resolution: "@react-types/combobox@npm:3.6.1" @@ -8604,19 +6677,6 @@ __metadata: languageName: node linkType: hard -"@react-types/datepicker@npm:^3.1.1": - version: 3.1.1 - resolution: "@react-types/datepicker@npm:3.1.1" - dependencies: - "@internationalized/date": ^3.0.1 - "@react-types/overlays": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: a3ab8ae22da8105ffebdebb5c89f212cda4d6f2203f7579cbd733e36afb4d2c7e4f986082becacaa66e7d1b4a99ef109952ddae7760d4bb0685e71d53894e316 - languageName: node - linkType: hard - "@react-types/datepicker@npm:^3.3.0": version: 3.3.0 resolution: "@react-types/datepicker@npm:3.3.0" @@ -8642,17 +6702,6 @@ __metadata: languageName: node linkType: hard -"@react-types/grid@npm:^3.1.3": - version: 3.1.3 - resolution: "@react-types/grid@npm:3.1.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 124b366436160ac7b88368a8be37abf4c703bde3fc1275e720f76d9ee8d0a10825fc5dd314b5eb6bb17b7d0c87091608d9b96d9521329ee5baeb94ab08fa3835 - languageName: node - linkType: hard - "@react-types/grid@npm:^3.1.7": version: 3.1.7 resolution: "@react-types/grid@npm:3.1.7" @@ -8709,18 +6758,6 @@ __metadata: languageName: node linkType: hard -"@react-types/menu@npm:^3.7.1": - version: 3.7.1 - resolution: "@react-types/menu@npm:3.7.1" - dependencies: - "@react-types/overlays": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 349443d1bd23bf64a9af57bef57d8ebfebfc6e82dbcef5cfd8ba778afc998f8dc3cebaae80728e6017b0d12b9e5aaea783254df36dd1b82a048b9e3c0e095795 - languageName: node - linkType: hard - "@react-types/menu@npm:^3.9.0": version: 3.9.0 resolution: "@react-types/menu@npm:3.9.0" @@ -8756,17 +6793,6 @@ __metadata: languageName: node linkType: hard -"@react-types/numberfield@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-types/numberfield@npm:3.3.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: b0f6627157dea0ce8a8fa3434c55bbbc69c4b46c84024d6da6f97091eae45c8b4f9b5b842c2ff82712c1b2e407b4acbd0d476ceda25abd3b9402a0b4573b3b52 - languageName: node - linkType: hard - "@react-types/numberfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/numberfield@npm:3.4.1" @@ -8778,17 +6804,6 @@ __metadata: languageName: node linkType: hard -"@react-types/overlays@npm:^3.6.3": - version: 3.6.3 - resolution: "@react-types/overlays@npm:3.6.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 8688db82adeda13e922f9805a5c9bd9f64e97e91c0ebf32409964e9d661828a4bb31907551dcdcd611807efa9824ff78aa8cb2ee4b0acfab001cbff5572336d4 - languageName: node - linkType: hard - "@react-types/overlays@npm:^3.7.1": version: 3.7.1 resolution: "@react-types/overlays@npm:3.7.1" @@ -8822,17 +6837,6 @@ __metadata: languageName: node linkType: hard -"@react-types/radio@npm:^3.2.3": - version: 3.2.3 - resolution: "@react-types/radio@npm:3.2.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: ce37d92a7e6665a9900b232aae68978bf1c82b4dffd30cc896c6382df7a9bb8a501a30f1b819d0630f9cbf21af3cb6f51de05fbaeaef4a1f250e5d39276eba59 - languageName: node - linkType: hard - "@react-types/radio@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/radio@npm:3.4.1" @@ -8844,18 +6848,6 @@ __metadata: languageName: node linkType: hard -"@react-types/searchfield@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-types/searchfield@npm:3.3.3" - dependencies: - "@react-types/shared": ^3.14.1 - "@react-types/textfield": ^3.5.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: cee59f6ad1da98cc01b81252ef91ebddf0a46df73e4cb3016474c9ad288a0d7b3de2d4607285de97ec23ecaba50391980268ac69b2def096c1fe3a33ecffc686 - languageName: node - linkType: hard - "@react-types/searchfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/searchfield@npm:3.4.1" @@ -8868,17 +6860,6 @@ __metadata: languageName: node linkType: hard -"@react-types/select@npm:^3.6.3": - version: 3.6.3 - resolution: "@react-types/select@npm:3.6.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 472d3086e13ca18857659c5a93e36d5e00c4f1077fd627b16ed93641e6ec39aed77a6cb819e3115616486df3178c2e725aef8dd95cd36fc297819b78111d10b8 - languageName: node - linkType: hard - "@react-types/select@npm:^3.8.0": version: 3.8.0 resolution: "@react-types/select@npm:3.8.0" @@ -8899,16 +6880,7 @@ __metadata: languageName: node linkType: hard -"@react-types/shared@npm:^3.14.1": - version: 3.14.1 - resolution: "@react-types/shared@npm:3.14.1" - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 117fe230f5a26b7fcaf535c1cfb7c4d42416b0f49d0e0b3436fef2a5851234967908c4e884fc5f2a99a04bee2543543348346a04e1f3f45aaa14c42b6f08491a - languageName: node - linkType: hard - -"@react-types/shared@npm:^3.18.0": +"@react-types/shared@npm:^3.14.1, @react-types/shared@npm:^3.18.0": version: 3.18.0 resolution: "@react-types/shared@npm:3.18.0" peerDependencies: @@ -8928,17 +6900,6 @@ __metadata: languageName: node linkType: hard -"@react-types/slider@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-types/slider@npm:3.2.1" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3c64ab2d99fd14debd74181ab4faef43656b274f00443899564e89f3b4b8d9c327184a9c236e4f69c4efc8cba0eca0a0aeae686dcf9a521a7749bab4e0bbdfbb - languageName: node - linkType: hard - "@react-types/slider@npm:^3.5.0": version: 3.5.0 resolution: "@react-types/slider@npm:3.5.0" @@ -8962,18 +6923,6 @@ __metadata: languageName: node linkType: hard -"@react-types/table@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-types/table@npm:3.3.1" - dependencies: - "@react-types/grid": ^3.1.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 1d3e4f8bac6e886944f67c159224893e63ec500f18aaadc74613d9053382c53fd282a7ee9dc21616b7fc0e1291d6ec7ccae87ebca2abdd19e8b371fb8cb46abc - languageName: node - linkType: hard - "@react-types/table@npm:^3.6.0": version: 3.6.0 resolution: "@react-types/table@npm:3.6.0" @@ -8986,17 +6935,6 @@ __metadata: languageName: node linkType: hard -"@react-types/tabs@npm:^3.1.3": - version: 3.1.3 - resolution: "@react-types/tabs@npm:3.1.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 04a95bfb92d2fe44900135bbdd1d256622c51fc90ebecc3374d29eb69bbb77ec13156d39b9fe1806e66b726cf5bbe9ff64822e04e5f3bcacd2429e27c3c260e1 - languageName: node - linkType: hard - "@react-types/tabs@npm:^3.2.1": version: 3.2.1 resolution: "@react-types/tabs@npm:3.2.1" @@ -9019,17 +6957,6 @@ __metadata: languageName: node linkType: hard -"@react-types/textfield@npm:^3.5.3": - version: 3.5.3 - resolution: "@react-types/textfield@npm:3.5.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f684821edba64e0b525606590800bf2cb6aea98c7304956ed3b2bbcb129ba7734897a9ca1bd056c2f23bf515399fed654071de6a2037942093c2af1c07fad1a9 - languageName: node - linkType: hard - "@react-types/textfield@npm:^3.7.1": version: 3.7.1 resolution: "@react-types/textfield@npm:3.7.1" @@ -9041,18 +6968,6 @@ __metadata: languageName: node linkType: hard -"@react-types/tooltip@npm:^3.2.3": - version: 3.2.3 - resolution: "@react-types/tooltip@npm:3.2.3" - dependencies: - "@react-types/overlays": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 5079ee2e561c2b9a7cc6e9dd22d48a26b0d61d79114aff730b7fc8348e199ec1db9e2ef96725f1682ebd5e93bbb223b920aa507f0f9997d8bc3df76f315077a6 - languageName: node - linkType: hard - "@react-types/tooltip@npm:^3.4.0": version: 3.4.0 resolution: "@react-types/tooltip@npm:3.4.0" @@ -9371,7 +7286,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.12, @rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.154": +"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9385,19 +7300,19 @@ __metadata: linkType: hard "@rocket.chat/css-in-js@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.103" + version: 0.31.23-dev.157 + resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.157" dependencies: "@emotion/hash": ^0.9.0 - "@rocket.chat/css-supports": ~0.31.23-dev.103 - "@rocket.chat/memo": ~0.31.23-dev.103 - "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.103 + "@rocket.chat/css-supports": ~0.31.23-dev.157 + "@rocket.chat/memo": ~0.31.23-dev.157 + "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.157 stylis: ~4.1.3 - checksum: 7fea932e9b60d186722f289989aead15fb6d8af2b11f75a44fefa3158c19d953ad789b161599b33bc5ede7c79c4d6de9b3453ca0dd1fd3ec453fe77891df4e55 + checksum: f573537eced6bd231e897012269c9e256a1ea8d2c9a65bc05806956dd5d2e8ed0586023721754d44ec1c897ab9551da7eae78f48763be92e123b4481036595ae languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.154": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9473,9 +7388,9 @@ __metadata: linkType: soft "@rocket.chat/emitter@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/emitter@npm:0.31.23-dev.103" - checksum: 5163602cf6793678e1e8b1165fbf4c77a2d24f2536af87d3ceb8464da99ce9bff2ccede931bfd6fde9857890678b82f530b29b088ed41ddac7f4a60363e46f1b + version: 0.31.23-dev.157 + resolution: "@rocket.chat/emitter@npm:0.31.23-dev.157" + checksum: badd657e886eacd3494a6f40fa67c33c346cbaacc412789ea1703f8ac632f490af2397e4f5c2f0234c3acf5c8fdd112ddb287ba0dd40e392ef4ba74e1ca8cb71 languageName: node linkType: hard @@ -9566,33 +7481,21 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-hooks@npm:next": - version: 0.32.0-dev.274 - resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.274" - dependencies: - use-sync-external-store: ~1.2.0 - peerDependencies: - "@rocket.chat/fuselage-tokens": "*" - react: ^17.0.2 - checksum: 32e0872deb05e8b853aed59cab2cc4b3d3a707f0997009170a12b19d252ca09c203f8139b227c90678c0c06e14228c116ab428b9fba81228d1dab981844d8d54 - languageName: node - linkType: hard - -"@rocket.chat/fuselage-hooks@npm:~0.32.0-dev.242": - version: 0.32.0-dev.242 - resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.242" +"@rocket.chat/fuselage-hooks@npm:next, @rocket.chat/fuselage-hooks@npm:~0.32.0-dev.296": + version: 0.32.0-dev.296 + resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.296" dependencies: use-sync-external-store: ~1.2.0 peerDependencies: "@rocket.chat/fuselage-tokens": "*" react: ^17.0.2 - checksum: f9dcfe53370b55c417668c32b08ddc81dcd6b8fbbc44d7968109906b3053985b34ee7d1ecdbea895b1d2eaaae7960fe2559cf0a6136799ea4725b986cb69895e + checksum: 85f379b87530bd61961794bba9e2b6c458e4101fa9983f3b643245ce8edfcfff6dbda7ecf1fa88ac7d7df6d927a18f113e0e9cdd50a648e9a061c7ac332fc9df languageName: node linkType: hard "@rocket.chat/fuselage-polyfills@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.103" + version: 0.31.23-dev.157 + resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.157" dependencies: "@juggle/resize-observer": ^3.4.0 clipboard-polyfill: ^3.0.3 @@ -9600,13 +7503,13 @@ __metadata: focus-visible: ^5.2.0 focus-within-polyfill: ^5.2.1 new-event-polyfill: ^1.0.1 - checksum: b4dbda8530718a9d585fbc38ed5d6efcb29d8df64982a7434117e11fc75df39c94faa666fd269cb1139d2431349e23fbb21dc5d7c100abb31f18eee27079d56c + checksum: 14da84dc80002d99736d3a148602cc5d82f1c830fdfef84b3db4aa11822c42a2c96e43a3aecb62a4eb222c750399d9f9349b224601c2f2011d375b5be76a3bf3 languageName: node linkType: hard "@rocket.chat/fuselage-toastbar@npm:next": - version: 0.32.0-dev.303 - resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.303" + version: 0.32.0-dev.357 + resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.357" peerDependencies: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" @@ -9614,21 +7517,14 @@ __metadata: "@rocket.chat/styled": "*" react: ^17.0.2 react-dom: ^17.0.2 - checksum: 479f5c50d1f6ce7a7e4cc4bbd0909f3bc1c3e83e3a9fdeb806d2b0540a17c46e30f8e72b816c954257411bd404f693d83bdf9b2499ce13cee2426d03c1dbec76 + checksum: 4a9c81ee42928b500fccec76542b0d812d9e79ab4ac9a89e8ee77ce19db45db78242218a799214f9248f1e04c636934ee70d44febc089ece38c35ac21837c101 languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:next": - version: 0.32.0-dev.280 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.280" - checksum: d8c7537512a7950ff6f0f3e23b3d6a2b82c0caea42d5b6e5599257fe2319cbd6f53a229c61413b025b20f576521aca67d9cba378f15e19edddae11a55133672a - languageName: node - linkType: hard - -"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.330": - version: 0.32.0-dev.330 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.330" - checksum: 9733eeb0c8f1d8220e13d20c85e9ecf219b6d22e3c339352a080a19cbdc7aae7828ae591884b070c48047fa984383122d8a816e7102a4c38ba6a6ce73f364452 +"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.333": + version: 0.32.0-dev.333 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.333" + checksum: dd19009e75e6154361be566ecea5f328902fdf25a10fdcc9ecd18076e424dd5f7d1a4b4c56b7679fe28e4a4ff77e32107d2f83a2c9c98f968ef6ff18f5de95db languageName: node linkType: hard @@ -9688,14 +7584,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.380 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.380" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.154 - "@rocket.chat/css-supports": ~0.31.23-dev.154 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.330 - "@rocket.chat/memo": ~0.31.23-dev.154 - "@rocket.chat/styled": ~0.31.23-dev.154 + version: 0.32.0-dev.383 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.383" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.157 + "@rocket.chat/css-supports": ~0.31.23-dev.157 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.333 + "@rocket.chat/memo": ~0.31.23-dev.157 + "@rocket.chat/styled": ~0.31.23-dev.157 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9707,7 +7603,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 07be707ad2fab881852d1b4ac49044915b24d5bf82352cc6e3e57526dfbcd13ec3b2d73b362bbe52d122aa5b45d90371838f1e463a443934d573022ac70a57e6 + checksum: 110c59c012dd019a6edddba2a1df6c4ceff6f5f84d5b4db206656ac06349bd5889ddd8ce2ee475bd91e947e457d28382fe938233891b9c81047ac3452ceedf93 languageName: node linkType: hard @@ -9795,9 +7691,9 @@ __metadata: linkType: soft "@rocket.chat/icons@npm:next": - version: 0.32.0-dev.362 - resolution: "@rocket.chat/icons@npm:0.32.0-dev.362" - checksum: 1c2096913e154d0db68b8ccc933294486ff06e250983809ce165f1497df05deec8b5165b47dbefdd013013b4cdf4a3a20f2ac191155b629e5ec4a7bcab5d8870 + version: 0.32.0-dev.365 + resolution: "@rocket.chat/icons@npm:0.32.0-dev.365" + checksum: 6323cadb2931582cbae4ec4cc271ae881a82915c9a1b0958a9a52c82ed5a5c3dad82699f3e0e22e580bc12695413e772eeb0a6cac670ab0c7863dd73d2f44cb0 languageName: node linkType: hard @@ -9815,14 +7711,14 @@ __metadata: linkType: soft "@rocket.chat/layout@npm:next": - version: 0.32.0-dev.213 - resolution: "@rocket.chat/layout@npm:0.32.0-dev.213" + version: 0.32.0-dev.266 + resolution: "@rocket.chat/layout@npm:0.32.0-dev.266" peerDependencies: "@rocket.chat/fuselage": "*" react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: 6a7e069f669fe7201e12ffb287064fe724e69294010b6cc426d4c5936f7e2c93cfe9ae92ee480e0ce7864cd5af37b5f9163dbdcefb850571adc3242ed897a5f1 + checksum: cbecb1564ea77578071dd2571ae3b004485232f035291c02f1b00fbee6c4ae12955c91a3feeb11b153f6b33ea5662b5691d4b7436cd2c8f40a36a8d990100a62 languageName: node linkType: hard @@ -9923,19 +7819,19 @@ __metadata: linkType: soft "@rocket.chat/logo@npm:next": - version: 0.32.0-dev.279 - resolution: "@rocket.chat/logo@npm:0.32.0-dev.279" + version: 0.32.0-dev.333 + resolution: "@rocket.chat/logo@npm:0.32.0-dev.333" dependencies: - "@rocket.chat/fuselage-hooks": ~0.32.0-dev.242 - "@rocket.chat/styled": ~0.31.23-dev.103 + "@rocket.chat/fuselage-hooks": ~0.32.0-dev.296 + "@rocket.chat/styled": ~0.31.23-dev.157 peerDependencies: react: 17.0.2 react-dom: 17.0.2 - checksum: a79a80a89111633f2c325ace46dc951fe67df94eb192a4ed691e274e19e951f7b22a9a301108f3db4daaac7667f8b9f5f2535a123a0ab72e94796358678acaeb + checksum: 502931f4aea0730e446aa17e22a881c9a19cbd15da4be5ec26c4fed51a4400fea9365ec5880c5fef3dc0f8a7ace6f1c1204f644a0b217b8eb1658de92214d1e6 languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.154": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -9943,18 +7839,18 @@ __metadata: linkType: hard "@rocket.chat/memo@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/memo@npm:0.31.23-dev.103" - checksum: 44b91bb7e02ffbeb2b094e5f283a1ddc3ca205f6ff01f5d044911b8cfc690b7e1472ee9f296de46bc84dcf2eb64d4c314813aa74d2076ec170ec1321f43145a6 + version: 0.31.23-dev.157 + resolution: "@rocket.chat/memo@npm:0.31.23-dev.157" + checksum: e1939d45e2759522463424c0ad876b5e7570a5c7bce561ee4f5e166b16778f794d54a1e2028dc90b1602ff2d20f32ae889b905e894b68af4df6de6844e1fb216 languageName: node linkType: hard "@rocket.chat/message-parser@npm:next": - version: 0.32.0-dev.296 - resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.296" + version: 0.32.0-dev.331 + resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.331" dependencies: tldts: ~5.7.112 - checksum: 2a40090b0b5b94e531da3bd1433c6f60b98f19b85eb9ca81db8706fcf360d8245c97fde2257a6c39e9be7fa4a630eede5abf679847ef3c923594c7156f8eb334 + checksum: dfb9fc430eba71c32b2ca1ea6357a3d42623a08dc41d9e9d23235fb0c71a939a0255ef3d2860df8f43d89fb23a34cccd0d66c470e01eaec4804b62410dba3808 languageName: node linkType: hard @@ -10432,8 +8328,8 @@ __metadata: linkType: soft "@rocket.chat/onboarding-ui@npm:next": - version: 0.32.0-dev.329 - resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.329" + version: 0.32.0-dev.383 + resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.383" dependencies: i18next: ~21.6.16 react-hook-form: ~7.27.1 @@ -10448,7 +8344,7 @@ __metadata: react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: 3118c3f3bb91db6e30748a1bf031221b2459b611c260446adf5bafcc9e38fa600a8a3366567a88183b2e93286b538233cb585f0d414b53f02e2eb48b8b2ba3ad + checksum: 427d4f4f6265a551e623129a118edc2170e689af12f68bb2c2c3e1b52905fdcacd950f34f7dba59173eef9ece4c21860d8366b76c3dc21a7c23f281bc74b3b9a languageName: node linkType: hard @@ -10725,22 +8621,22 @@ __metadata: linkType: soft "@rocket.chat/string-helpers@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.103" - checksum: b505564152613d5871dae226b67458d69fbacf2ddeb3f6d10cf81124ccccfd75ad60133bc772db6bf20f4b17f7cfe09fc0ea9c846c2f519b031ce22d0da6688e + version: 0.31.23-dev.157 + resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.157" + checksum: 41d49dcc57aaea77f71928f7c5da8c68deebafe4b33718f98d6af4427c7df6e212bf0dca0703d74d9cf7fb24c65ec8215e37f8240544754963e0a83e5d7f62f6 languageName: node linkType: hard "@rocket.chat/styled@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/styled@npm:0.31.23-dev.103" + version: 0.31.23-dev.157 + resolution: "@rocket.chat/styled@npm:0.31.23-dev.157" dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.103 - checksum: aadeb2ffe37e01d694012ec49d301a9a8960b82377fc1daa2c332b657414c4c52839010d3f8ce5114312065b8da0cc41b70bdc93fa1916169c53d333d56428bc + "@rocket.chat/css-in-js": ~0.31.23-dev.157 + checksum: ab4aaa77b8570111d37233954322e036316bde3bf65cf781e242ca936ec84f4dca4fed7d91cbd7ff4b2aab329f812d7887003f721a9c72fd4544bbc9fbf342af languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.154": +"@rocket.chat/styled@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: @@ -10749,7 +8645,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/stylis-logical-props-middleware@npm:^0.31.23, @rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.103": +"@rocket.chat/stylis-logical-props-middleware@npm:^0.31.23, @rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23" dependencies: @@ -10883,9 +8779,9 @@ __metadata: linkType: soft "@rocket.chat/ui-kit@npm:next": - version: 0.32.0-dev.294 - resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.294" - checksum: cf58890f3fbcfdff3df9436a33c4aa333de34369f89902107dee74c50c11b8af793c9c80a8df5046ffcabf0fd90111f31a6fc2030876e838e43ec4c6a5703fc4 + version: 0.32.0-dev.318 + resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.318" + checksum: 4cb64edb3942635e23ba904da95eb5b742b1cadeb89c1aad7fb2acc60b25886cf83cfcd9c785f6a57959e1e3bcd713ac8014ffe45b70bc6d40b1651a206fe6bc languageName: node linkType: hard @@ -10979,7 +8875,7 @@ __metadata: "@codemirror/lang-json": ^6.0.1 "@codemirror/tooltip": ^0.19.16 "@lezer/highlight": ^1.1.6 - "@rocket.chat/css-in-js": ^0.31.12 + "@rocket.chat/css-in-js": next "@rocket.chat/fuselage": next "@rocket.chat/fuselage-hooks": next "@rocket.chat/fuselage-polyfills": next @@ -12874,33 +10770,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7": - version: 7.20.0 - resolution: "@types/babel__core@npm:7.20.0" - dependencies: - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 - "@types/babel__generator": "*" - "@types/babel__template": "*" - "@types/babel__traverse": "*" - checksum: 49b601a0a7637f1f387442c8156bd086cfd10ff4b82b0e1994e73a6396643b5435366fb33d6b604eade8467cca594ef97adcbc412aede90bb112ebe88d0ad6df - languageName: node - linkType: hard - -"@types/babel__core@npm:^7.1.14": - version: 7.1.20 - resolution: "@types/babel__core@npm:7.1.20" - dependencies: - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 - "@types/babel__generator": "*" - "@types/babel__template": "*" - "@types/babel__traverse": "*" - checksum: a09c4f0456552547a5b8a5a009a3daec4d362f622168f8e08bda0ded2da0a65ab0b1642e23c433b3616721f5701dc34a996c5bde5baeaea53eda98f438043f2c - languageName: node - linkType: hard - -"@types/babel__core@npm:~7.20.1": +"@types/babel__core@npm:^7, @types/babel__core@npm:^7.1.14, @types/babel__core@npm:~7.20.1": version: 7.20.1 resolution: "@types/babel__core@npm:7.20.1" dependencies: @@ -13037,14 +10907,7 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:*": - version: 4.3.1 - resolution: "@types/chai@npm:4.3.1" - checksum: 2ee246b76c469cd620a7a1876a73bc597074361b67d547b4bd96a0c1adb43597ede2d8589ab626192e14349d83cbb646cc11e2c179eeeb43ff11596de94d82c4 - languageName: node - linkType: hard - -"@types/chai@npm:^4.3.5": +"@types/chai@npm:*, @types/chai@npm:^4.3.5": version: 4.3.5 resolution: "@types/chai@npm:4.3.5" checksum: c8f26a88c6b5b53a3275c7f5ff8f107028e3cbb9ff26795fff5f3d9dea07106a54ce9e2dce5e40347f7c4cc35657900aaf0c83934a25a1ae12e61e0f5516e431 @@ -13187,17 +11050,7 @@ __metadata: languageName: node linkType: hard -"@types/eslint@npm:*": - version: 8.37.0 - resolution: "@types/eslint@npm:8.37.0" - dependencies: - "@types/estree": "*" - "@types/json-schema": "*" - checksum: 06d3b3fba12004294591b5c7a52e3cec439472195da54e096076b1f2ddfbb8a445973b9681046dd530a6ac31eca502f635abc1e3ce37d03513089358e6f822ee - languageName: node - linkType: hard - -"@types/eslint@npm:^8.40.2, @types/eslint@npm:~8.40.2": +"@types/eslint@npm:*, @types/eslint@npm:^8.40.2, @types/eslint@npm:~8.40.2": version: 8.40.2 resolution: "@types/eslint@npm:8.40.2" dependencies: @@ -13207,20 +11060,20 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^0.0.51": - version: 0.0.51 - resolution: "@types/estree@npm:0.0.51" - checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 - languageName: node - linkType: hard - -"@types/estree@npm:^1.0.0": +"@types/estree@npm:*, @types/estree@npm:^1.0.0": version: 1.0.1 resolution: "@types/estree@npm:1.0.1" checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d languageName: node linkType: hard +"@types/estree@npm:^0.0.51": + version: 0.0.51 + resolution: "@types/estree@npm:0.0.51" + checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 + languageName: node + linkType: hard + "@types/express-rate-limit@npm:^5.1.3": version: 5.1.3 resolution: "@types/express-rate-limit@npm:5.1.3" @@ -13230,18 +11083,7 @@ __metadata: languageName: node linkType: hard -"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.18": - version: 4.17.31 - resolution: "@types/express-serve-static-core@npm:4.17.31" - dependencies: - "@types/node": "*" - "@types/qs": "*" - "@types/range-parser": "*" - checksum: 009bfbe1070837454a1056aa710d0390ee5fb8c05dfe5a1691cc3e2ca88dc256f80e1ca27cb51a978681631d2f6431bfc9ec352ea46dd0c6eb183d0170bde5df - languageName: node - linkType: hard - -"@types/express-serve-static-core@npm:^4.17.33": +"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": version: 4.17.35 resolution: "@types/express-serve-static-core@npm:4.17.35" dependencies: @@ -13253,19 +11095,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.8": - version: 4.17.13 - resolution: "@types/express@npm:4.17.13" - dependencies: - "@types/body-parser": "*" - "@types/express-serve-static-core": ^4.17.18 - "@types/qs": "*" - "@types/serve-static": "*" - checksum: 12a2a0e6c4b993fc0854bec665906788aea0d8ee4392389d7a98a5de1eefdd33c9e1e40a91f3afd274011119c506f7b4126acb97fae62ae20b654974d44cba12 - languageName: node - linkType: hard - -"@types/express@npm:^4.17.17": +"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.17, @types/express@npm:^4.17.8": version: 4.17.17 resolution: "@types/express@npm:4.17.17" dependencies: @@ -13434,13 +11264,13 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*": - version: 29.5.0 - resolution: "@types/jest@npm:29.5.0" +"@types/jest@npm:*, @types/jest@npm:^29.5.1, @types/jest@npm:^29.5.2, @types/jest@npm:~29.5.2": + version: 29.5.2 + resolution: "@types/jest@npm:29.5.2" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: cd877e5c56d299cceb8bfdcbb1a77723c706750dd3c3bc47403bc3599b8faff590a3b009c68bb5b11bf7a8c77d1fb01de5e124329b4a08e65f1cdda28b0ecdb8 + checksum: 7d205599ea3cccc262bad5cc173d3242d6bf8138c99458509230e4ecef07a52d6ddcde5a1dbd49ace655c0af51d2dbadef3748697292ea4d86da19d9e03e19c0 languageName: node linkType: hard @@ -13454,16 +11284,6 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:^29.5.1, @types/jest@npm:^29.5.2, @types/jest@npm:~29.5.2": - version: 29.5.2 - resolution: "@types/jest@npm:29.5.2" - dependencies: - expect: ^29.0.0 - pretty-format: ^29.0.0 - checksum: 7d205599ea3cccc262bad5cc173d3242d6bf8138c99458509230e4ecef07a52d6ddcde5a1dbd49ace655c0af51d2dbadef3748697292ea4d86da19d9e03e19c0 - languageName: node - linkType: hard - "@types/jquery@npm:*": version: 3.5.14 resolution: "@types/jquery@npm:3.5.14" @@ -13596,14 +11416,7 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:*, @types/lodash@npm:^4.14.167": - version: 4.14.182 - resolution: "@types/lodash@npm:4.14.182" - checksum: 7dd137aa9dbabd632408bd37009d984655164fa1ecc3f2b6eb94afe35bf0a5852cbab6183148d883e9c73a958b7fec9a9bcf7c8e45d41195add6a18c34958209 - languageName: node - linkType: hard - -"@types/lodash@npm:^4.14.195": +"@types/lodash@npm:*, @types/lodash@npm:^4.14.167, @types/lodash@npm:^4.14.195": version: 4.14.195 resolution: "@types/lodash@npm:4.14.195" checksum: 39b75ca635b3fa943d17d3d3aabc750babe4c8212485a4df166fe0516e39288e14b0c60afc6e21913cc0e5a84734633c71e617e2bd14eaa1cf51b8d7799c432e @@ -13762,44 +11575,21 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^14.0.10 || ^16.0.0, @types/node@npm:^14.14.20 || ^16.0.0": - version: 16.11.39 - resolution: "@types/node@npm:16.11.39" - checksum: bc97b9773ac6b3194800f990b349fad7f66c6126dacef59291b10a2c8b6813d6f67f947b7e12a6c9952790f7065d576fe38355b8fe034a6af60f317cfc570f69 - languageName: node - linkType: hard - -"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37": - version: 14.18.21 - resolution: "@types/node@npm:14.18.21" - checksum: 4ed35b76609647a4e36a194702e31cdda9ed42174ddaf7937bc5498984e98a99e8a42ea895ea17dd9c5ec18080112c29ab670c34f90eb9f7a4703b85b31e34fa - languageName: node - linkType: hard - -"@types/node@npm:^14.18.51": - version: 14.18.51 - resolution: "@types/node@npm:14.18.51" - checksum: 0960a31d2ac605763fe79c8edcee3cb48257d345ce417c019d84ff5d8cd92dd0937674814ab3f169346b4259c29f640556006bcb2c54cfb3e63fa0cf728d320e - languageName: node - linkType: hard - -"@types/node@npm:^16.18.36": +"@types/node@npm:^14.0.10 || ^16.0.0, @types/node@npm:^14.14.20 || ^16.0.0, @types/node@npm:^16.18.36": version: 16.18.36 resolution: "@types/node@npm:16.18.36" checksum: a9d138fa1269079c60daad6984713dc0b713983f8b34a83edbc6d7957b2e38beab9b2598c9fe99f19d073e20bc212a18aaf82eabdc23ef64dce7d2089a9aab2a languageName: node linkType: hard -"@types/nodemailer@npm:*": - version: 6.4.7 - resolution: "@types/nodemailer@npm:6.4.7" - dependencies: - "@types/node": "*" - checksum: dc2a33a89135e04a5bea4921e8645e8453b90e3c3b05f0646f05071c5951ab697ea49ea1e503a690f04cb0a6abfc54967325c5a4036356793cfbb64ba64fb141 +"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37, @types/node@npm:^14.18.51": + version: 14.18.51 + resolution: "@types/node@npm:14.18.51" + checksum: 0960a31d2ac605763fe79c8edcee3cb48257d345ce417c019d84ff5d8cd92dd0937674814ab3f169346b4259c29f640556006bcb2c54cfb3e63fa0cf728d320e languageName: node linkType: hard -"@types/nodemailer@npm:^6.4.8": +"@types/nodemailer@npm:*, @types/nodemailer@npm:^6.4.8": version: 6.4.8 resolution: "@types/nodemailer@npm:6.4.8" dependencies: @@ -13975,16 +11765,7 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:<18.0.0": - version: 17.0.17 - resolution: "@types/react-dom@npm:17.0.17" - dependencies: - "@types/react": ^17 - checksum: 23caf98aa03e968811560f92a2c8f451694253ebe16b670929b24eaf0e7fa62ba549abe9db0ac028a9d8a9086acd6ab9c6c773f163fa21224845edbc00ba6232 - languageName: node - linkType: hard - -"@types/react-dom@npm:^17.0.20, @types/react-dom@npm:~17.0.20": +"@types/react-dom@npm:<18.0.0, @types/react-dom@npm:^17.0.20, @types/react-dom@npm:~17.0.20": version: 17.0.20 resolution: "@types/react-dom@npm:17.0.20" dependencies: @@ -13993,16 +11774,7 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.0.0": - version: 18.0.10 - resolution: "@types/react-dom@npm:18.0.10" - dependencies: - "@types/react": "*" - checksum: ff8282d5005a0b1cd95fb65bf79d3d8485e4cfe2aaf052129033a178684b940014a3f4536bc20d573f8a01cf4c6f4770c74988cef7c2b5cac3041d9f172647e3 - languageName: node - linkType: hard - -"@types/react-dom@npm:^18.2.5": +"@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.2.5": version: 18.2.5 resolution: "@types/react-dom@npm:18.2.5" dependencies: @@ -14023,18 +11795,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^17": - version: 17.0.58 - resolution: "@types/react@npm:17.0.58" - dependencies: - "@types/prop-types": "*" - "@types/scheduler": "*" - csstype: ^3.0.2 - checksum: 4eaf32b86c43f388c681e34a00921c508dd88a1d1022aebfadc5fe802b7c5bed863de1a17eed31e43ca2d65222952dfe79a022055a0e6e4e1ad89fc5a42ec05e - languageName: node - linkType: hard - -"@types/react@npm:^17.0.62, @types/react@npm:~17.0.62": +"@types/react@npm:*, @types/react@npm:^17, @types/react@npm:^17.0.62, @types/react@npm:~17.0.62": version: 17.0.62 resolution: "@types/react@npm:17.0.62" dependencies: @@ -14260,16 +12021,7 @@ __metadata: languageName: node linkType: hard -"@types/testing-library__jest-dom@npm:^5.9.1": - version: 5.14.5 - resolution: "@types/testing-library__jest-dom@npm:5.14.5" - dependencies: - "@types/jest": "*" - checksum: dcb05416758fe88c1f4f3aa97b4699fcb46a5ed8f53c6b81721e66155452a48caf12ecb97dfdfd4130678e65efd66b9fca0ac434b3d63affec84842a84a6bf38 - languageName: node - linkType: hard - -"@types/testing-library__jest-dom@npm:~5.14.6": +"@types/testing-library__jest-dom@npm:^5.9.1, @types/testing-library__jest-dom@npm:~5.14.6": version: 5.14.6 resolution: "@types/testing-library__jest-dom@npm:5.14.6" dependencies: @@ -14422,16 +12174,7 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.1": - version: 8.5.4 - resolution: "@types/ws@npm:8.5.4" - dependencies: - "@types/node": "*" - checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1 - languageName: node - linkType: hard - -"@types/ws@npm:^8.5.5": +"@types/ws@npm:^8.5.1, @types/ws@npm:^8.5.5": version: 8.5.5 resolution: "@types/ws@npm:8.5.5" dependencies: @@ -14534,16 +12277,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/scope-manager@npm:5.58.0" - dependencies: - "@typescript-eslint/types": 5.58.0 - "@typescript-eslint/visitor-keys": 5.58.0 - checksum: f0d3df5cc3c461fe63ef89ad886b53c239cc7c1d9061d83d8a9d9c8e087e5501eac84bebff8a954728c17ccea191f235686373d54d2b8b6370af2bcf2b18e062 - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/scope-manager@npm:5.60.0" @@ -14571,20 +12304,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/types@npm:5.52.0" - checksum: 018940d61aebf7cf3f7de1b9957446e2ea01f08fe950bef4788c716a3a88f7c42765fe7d80152b0d0428fcd4bd3ace2dfa8c459ba1c59d9a84e951642180f869 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/types@npm:5.58.0" - checksum: 8622a73d73220c4a7111537825f488c0271272032a1d4e129dc722bc6e8b3ec84f64469b2ca3b8dae7da3a9c18953ce1449af51f5f757dad60835eb579ad1d2c - languageName: node - linkType: hard - "@typescript-eslint/types@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/types@npm:5.60.0" @@ -14592,41 +12311,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.52.0" - dependencies: - "@typescript-eslint/types": 5.52.0 - "@typescript-eslint/visitor-keys": 5.52.0 - debug: ^4.3.4 - globby: ^11.1.0 - is-glob: ^4.0.3 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 67d396907fee3d6894e26411a5098a37f07e5d50343189e6361ff7db91c74a7ffe2abd630d11f14c2bda1f4af13edf52b80b11cbccb55b44079c7cec14c9e108 - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.58.0" - dependencies: - "@typescript-eslint/typescript-estree": 5.52.0 - "@typescript-eslint/utils": 5.52.0 - debug: ^4.3.4 - tsutils: ^3.21.0 - peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: 51b668ec858db0c040a71dff526273945cee4ba5a9b240528d503d02526685882d900cf071c6636a4d9061ed3fd4a7274f7f1a23fba55c4b48b143344b4009c7 - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/typescript-estree@npm:5.60.0" @@ -14645,25 +12329,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/utils@npm:5.52.0" - dependencies: - "@eslint-community/eslint-utils": ^4.2.0 - "@types/json-schema": ^7.0.9 - "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.58.0 - "@typescript-eslint/types": 5.58.0 - "@typescript-eslint/typescript-estree": 5.58.0 - eslint-scope: ^5.1.1 - semver: ^7.3.7 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 01906be5262ece36537e9d586e4d2d4791e05752a9354bcb42b1f5bf965f53daa13309c61c3dff5e201ea28c298e4e01cf0c93738afa0099fea0da3b1d8cb3a5 - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:5.60.0": +"@typescript-eslint/utils@npm:5.60.0, @typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.45.0, @typescript-eslint/utils@npm:^5.58.0": version: 5.60.0 resolution: "@typescript-eslint/utils@npm:5.60.0" dependencies: @@ -14681,44 +12347,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.45.0, @typescript-eslint/utils@npm:^5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/utils@npm:5.58.0" - dependencies: - "@eslint-community/eslint-utils": ^4.2.0 - "@types/json-schema": ^7.0.9 - "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.58.0 - "@typescript-eslint/types": 5.58.0 - "@typescript-eslint/typescript-estree": 5.58.0 - eslint-scope: ^5.1.1 - semver: ^7.3.7 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: c618ae67963ecf96b1492c09afaeb363f542f0d6780bcac4af3c26034e3b20034666b2d523aa94821df813aafb57a0b150a7d5c2224fe8257452ad1de2237a58 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.52.0" - dependencies: - "@typescript-eslint/types": 5.52.0 - eslint-visitor-keys: ^3.3.0 - checksum: 33b44f0cd35b7b47f34e89d52e47b8d8200f55af306b22db4de104d79f65907458ea022e548f50d966e32fea150432ac9c1ae65b3001b0ad2ac8a17c0211f370 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.58.0" - dependencies: - "@typescript-eslint/types": 5.58.0 - eslint-visitor-keys: ^3.3.0 - checksum: ab2d1f37660559954c840429ef78bbf71834063557e3e68e435005b4987970b9356fdf217ead53f7a57f66f5488dc478062c5c44bf17053a8bf041733539b98f - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/visitor-keys@npm:5.60.0" @@ -14768,16 +12396,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/ast@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ast@npm:1.11.1" - dependencies: - "@webassemblyjs/helper-numbers": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - checksum: 1eee1534adebeece635362f8e834ae03e389281972611408d64be7895fc49f48f98fddbbb5339bf8a72cb101bcb066e8bca3ca1bf1ef47dadf89def0395a8d87 - languageName: node - linkType: hard - "@webassemblyjs/ast@npm:1.11.6, @webassemblyjs/ast@npm:^1.11.5": version: 1.11.6 resolution: "@webassemblyjs/ast@npm:1.11.6" @@ -14799,13 +12417,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/floating-point-hex-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.1" - checksum: b8efc6fa08e4787b7f8e682182d84dfdf8da9d9c77cae5d293818bc4a55c1f419a87fa265ab85252b3e6c1fd323d799efea68d825d341a7c365c64bc14750e97 - languageName: node - linkType: hard - "@webassemblyjs/floating-point-hex-parser@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.6" @@ -14820,13 +12431,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-api-error@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-api-error@npm:1.11.1" - checksum: 0792813f0ed4a0e5ee0750e8b5d0c631f08e927f4bdfdd9fe9105dc410c786850b8c61bff7f9f515fdfb149903bec3c976a1310573a4c6866a94d49bc7271959 - languageName: node - linkType: hard - "@webassemblyjs/helper-api-error@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-api-error@npm:1.11.6" @@ -14841,13 +12445,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-buffer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-buffer@npm:1.11.1" - checksum: a337ee44b45590c3a30db5a8b7b68a717526cf967ada9f10253995294dbd70a58b2da2165222e0b9830cd4fc6e4c833bf441a721128d1fe2e9a7ab26b36003ce - languageName: node - linkType: hard - "@webassemblyjs/helper-buffer@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-buffer@npm:1.11.6" @@ -14887,17 +12484,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-numbers@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-numbers@npm:1.11.1" - dependencies: - "@webassemblyjs/floating-point-hex-parser": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 - "@xtuc/long": 4.2.2 - checksum: 44d2905dac2f14d1e9b5765cf1063a0fa3d57295c6d8930f6c59a36462afecc6e763e8a110b97b342a0f13376166c5d41aa928e6ced92e2f06b071fd0db59d3a - languageName: node - linkType: hard - "@webassemblyjs/helper-numbers@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-numbers@npm:1.11.6" @@ -14909,13 +12495,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-wasm-bytecode@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.1" - checksum: eac400113127832c88f5826bcc3ad1c0db9b3dbd4c51a723cfdb16af6bfcbceb608170fdaac0ab7731a7e18b291be7af68a47fcdb41cfe0260c10857e7413d97 - languageName: node - linkType: hard - "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6" @@ -14930,18 +12509,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-wasm-section@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - checksum: 617696cfe8ecaf0532763162aaf748eb69096fb27950219bb87686c6b2e66e11cd0614d95d319d0ab1904bc14ebe4e29068b12c3e7c5e020281379741fe4bedf - languageName: node - linkType: hard - "@webassemblyjs/helper-wasm-section@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.6" @@ -14966,15 +12533,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/ieee754@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ieee754@npm:1.11.1" - dependencies: - "@xtuc/ieee754": ^1.2.0 - checksum: 23a0ac02a50f244471631802798a816524df17e56b1ef929f0c73e3cde70eaf105a24130105c60aff9d64a24ce3b640dad443d6f86e5967f922943a7115022ec - languageName: node - linkType: hard - "@webassemblyjs/ieee754@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/ieee754@npm:1.11.6" @@ -14993,15 +12551,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/leb128@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/leb128@npm:1.11.1" - dependencies: - "@xtuc/long": 4.2.2 - checksum: 33ccc4ade2f24de07bf31690844d0b1ad224304ee2062b0e464a610b0209c79e0b3009ac190efe0e6bd568b0d1578d7c3047fc1f9d0197c92fc061f56224ff4a - languageName: node - linkType: hard - "@webassemblyjs/leb128@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/leb128@npm:1.11.6" @@ -15020,13 +12569,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/utf8@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/utf8@npm:1.11.1" - checksum: 972c5cfc769d7af79313a6bfb96517253a270a4bf0c33ba486aa43cac43917184fb35e51dfc9e6b5601548cd5931479a42e42c89a13bb591ffabebf30c8a6a0b - languageName: node - linkType: hard - "@webassemblyjs/utf8@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/utf8@npm:1.11.6" @@ -15041,22 +12583,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-edit@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-edit@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/helper-wasm-section": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-opt": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - "@webassemblyjs/wast-printer": 1.11.1 - checksum: 6d7d9efaec1227e7ef7585a5d7ff0be5f329f7c1c6b6c0e906b18ed2e9a28792a5635e450aca2d136770d0207225f204eff70a4b8fd879d3ac79e1dcc26dbeb9 - languageName: node - linkType: hard - "@webassemblyjs/wasm-edit@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/wasm-edit@npm:1.9.0" @@ -15089,19 +12615,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-gen@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-gen@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1f6921e640293bf99fb16b21e09acb59b340a79f986c8f979853a0ae9f0b58557534b81e02ea2b4ef11e929d946708533fd0693c7f3712924128fdafd6465f5b - languageName: node - linkType: hard - "@webassemblyjs/wasm-gen@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wasm-gen@npm:1.11.6" @@ -15128,18 +12641,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-opt@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-opt@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - checksum: 21586883a20009e2b20feb67bdc451bbc6942252e038aae4c3a08e6f67b6bae0f5f88f20bfc7bd0452db5000bacaf5ab42b98cf9aa034a6c70e9fc616142e1db - languageName: node - linkType: hard - "@webassemblyjs/wasm-opt@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wasm-opt@npm:1.11.6" @@ -15164,20 +12665,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-parser@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1521644065c360e7b27fad9f4bb2df1802d134dd62937fa1f601a1975cde56bc31a57b6e26408b9ee0228626ff3ba1131ae6f74ffb7d718415b6528c5a6dbfc2 - languageName: node - linkType: hard - "@webassemblyjs/wasm-parser@npm:1.11.6, @webassemblyjs/wasm-parser@npm:^1.11.5": version: 1.11.6 resolution: "@webassemblyjs/wasm-parser@npm:1.11.6" @@ -15220,16 +12707,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wast-printer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wast-printer@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@xtuc/long": 4.2.2 - checksum: f15ae4c2441b979a3b4fce78f3d83472fb22350c6dc3fd34bfe7c3da108e0b2360718734d961bba20e7716cb8578e964b870da55b035e209e50ec9db0378a3f7 - languageName: node - linkType: hard - "@webassemblyjs/wast-printer@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wast-printer@npm:1.11.6" @@ -15284,14 +12761,14 @@ __metadata: languageName: node linkType: hard -"@xmldom/xmldom@npm:0.8.7, @xmldom/xmldom@npm:^0.8.5": +"@xmldom/xmldom@npm:0.8.7": version: 0.8.7 resolution: "@xmldom/xmldom@npm:0.8.7" checksum: 593d4429c2281ee7799adcb6ff8604b68cf30ce0721537e3e380287b423e67c7ac197d90987f932b4fd3febc409ded8435706e7f90fbba6e22e08740477341d1 languageName: node linkType: hard -"@xmldom/xmldom@npm:^0.8.8": +"@xmldom/xmldom@npm:^0.8.5, @xmldom/xmldom@npm:^0.8.8": version: 0.8.8 resolution: "@xmldom/xmldom@npm:0.8.8" checksum: 5f5fc0482fcc599f62e3009516932a265e00f1bb2093fe2c76f3f8d9bfebdd13246f48d4132c9b301c7a573f0fa8712e56aa747dce75b179c2b73f1dde7b5f42 @@ -15386,15 +12863,6 @@ __metadata: languageName: node linkType: hard -"acorn-import-assertions@npm:^1.7.6": - version: 1.8.0 - resolution: "acorn-import-assertions@npm:1.8.0" - peerDependencies: - acorn: ^8 - checksum: 5c4cf7c850102ba7ae0eeae0deb40fb3158c8ca5ff15c0bca43b5c47e307a1de3d8ef761788f881343680ea374631ae9e9615ba8876fee5268dbe068c98bcba6 - languageName: node - linkType: hard - "acorn-import-assertions@npm:^1.9.0": version: 1.9.0 resolution: "acorn-import-assertions@npm:1.9.0" @@ -15445,16 +12913,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1": - version: 8.8.2 - resolution: "acorn@npm:8.8.2" - bin: - acorn: bin/acorn - checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 - languageName: node - linkType: hard - -"acorn@npm:^8.8.2": +"acorn@npm:^8.1.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1, acorn@npm:^8.8.2": version: 8.9.0 resolution: "acorn@npm:8.9.0" bin: @@ -15606,7 +13065,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.8.0": +"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.11.0, ajv@npm:^8.8.0": version: 8.12.0 resolution: "ajv@npm:8.12.0" dependencies: @@ -15618,18 +13077,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.1, ajv@npm:^8.11.0": - version: 8.11.0 - resolution: "ajv@npm:8.11.0" - dependencies: - fast-deep-equal: ^3.1.1 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef - languageName: node - linkType: hard - "alphanum-sort@npm:^1.0.0": version: 1.0.2 resolution: "alphanum-sort@npm:1.0.2" @@ -15685,7 +13132,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": +"ansi-colors@npm:4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 @@ -15699,7 +13146,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:^4.1.3": +"ansi-colors@npm:^4.1.1, ansi-colors@npm:^4.1.3": version: 4.1.3 resolution: "ansi-colors@npm:4.1.3" checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e @@ -16017,19 +13464,7 @@ __metadata: languageName: node linkType: hard -"args@npm:^5.0.1": - version: 5.0.1 - resolution: "args@npm:5.0.1" - dependencies: - camelcase: 5.0.0 - chalk: 2.4.2 - leven: 2.1.0 - mri: 1.1.4 - checksum: 51e2a05f32d15b8e292f000e6b232118df61b8f4fd446b17bb4e99df9ab47fe2c4a01924d7f967a6f08e82f9c19be277b08ed22bceff058aca849144ef8efed3 - languageName: node - linkType: hard - -"args@npm:^5.0.3": +"args@npm:^5.0.1, args@npm:^5.0.3": version: 5.0.3 resolution: "args@npm:5.0.3" dependencies: @@ -16652,19 +14087,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.3.3": - version: 0.3.3 - resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" - dependencies: - "@babel/compat-data": ^7.17.7 - "@babel/helper-define-polyfill-provider": ^0.3.3 - semver: ^6.1.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7db3044993f3dddb3cc3d407bc82e640964a3bfe22de05d90e1f8f7a5cb71460011ab136d3c03c6c1ba428359ebf635688cd6205e28d0469bba221985f5c6179 - languageName: node - linkType: hard - "babel-plugin-polyfill-corejs2@npm:^0.4.3": version: 0.4.3 resolution: "babel-plugin-polyfill-corejs2@npm:0.4.3" @@ -16690,18 +14112,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.6.0": - version: 0.6.0 - resolution: "babel-plugin-polyfill-corejs3@npm:0.6.0" - dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.3 - core-js-compat: ^3.25.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 470bb8c59f7c0912bd77fe1b5a2e72f349b3f65bbdee1d60d6eb7e1f4a085c6f24b2dd5ab4ac6c2df6444a96b070ef6790eccc9edb6a2668c60d33133bfb62c6 - languageName: node - linkType: hard - "babel-plugin-polyfill-corejs3@npm:^0.8.1": version: 0.8.1 resolution: "babel-plugin-polyfill-corejs3@npm:0.8.1" @@ -16714,17 +14124,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.4.1": - version: 0.4.1 - resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" - dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ab0355efbad17d29492503230387679dfb780b63b25408990d2e4cf421012dae61d6199ddc309f4d2409ce4e9d3002d187702700dd8f4f8770ebbba651ed066c - languageName: node - linkType: hard - "babel-plugin-polyfill-regenerator@npm:^0.5.0": version: 0.5.0 resolution: "babel-plugin-polyfill-regenerator@npm:0.5.0" @@ -17187,7 +14586,7 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.20.2, body-parser@npm:^1.20.2": +"body-parser@npm:1.20.2, body-parser@npm:^1.19.0, body-parser@npm:^1.20.2": version: 1.20.2 resolution: "body-parser@npm:1.20.2" dependencies: @@ -17207,26 +14606,6 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:^1.19.0": - version: 1.20.0 - resolution: "body-parser@npm:1.20.0" - dependencies: - bytes: 3.1.2 - content-type: ~1.0.4 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.10.3 - raw-body: 2.5.1 - type-is: ~1.6.18 - unpipe: 1.0.0 - checksum: 12fffdeac82fe20dddcab7074215d5156e7d02a69ae90cbe9fee1ca3efa2f28ef52097cbea76685ee0a1509c71d85abd0056a08e612c09077cad6277a644cf88 - languageName: node - linkType: hard - "bonjour-service@npm:^1.0.11": version: 1.1.1 resolution: "bonjour-service@npm:1.1.1" @@ -17481,21 +14860,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.3": - version: 4.21.3 - resolution: "browserslist@npm:4.21.3" - dependencies: - caniuse-lite: ^1.0.30001370 - electron-to-chromium: ^1.4.202 - node-releases: ^2.0.6 - update-browserslist-db: ^1.0.5 - bin: - browserslist: cli.js - checksum: ff512a7bcca1c530e2854bbdfc7be2791d0fb524097a6340e56e1d5924164c7e4e0a9b070de04cdc4c149d15cb4d4275cb7c626ebbce954278a2823aaad2452a - languageName: node - linkType: hard - -"browserslist@npm:^4.21.5": +"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.3, browserslist@npm:^4.21.5": version: 4.21.9 resolution: "browserslist@npm:4.21.9" dependencies: @@ -18002,14 +15367,7 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001370": - version: 1.0.30001464 - resolution: "caniuse-lite@npm:1.0.30001464" - checksum: 67cdee102c1660d62d7b9dbd4740bb7af096236618f2509fd2e0039d50db5f02fb87c21d90b6d573fdcf50deaf3c84503d009e871502b5c221d0ba1dec18ba11 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001503": +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001503": version: 1.0.30001503 resolution: "caniuse-lite@npm:1.0.30001503" checksum: cd5f0af37655ff71ec4ab3c49124d75e0b8b68de625d07ea80e9a82329e616b5203d5dad6865192653be9da50081c06878f081ab069dac0be35adf29aa1599cd @@ -18115,22 +15473,7 @@ __metadata: languageName: node linkType: hard -"chai@npm:>1.9.0": - version: 4.3.6 - resolution: "chai@npm:4.3.6" - dependencies: - assertion-error: ^1.1.0 - check-error: ^1.0.2 - deep-eql: ^3.0.1 - get-func-name: ^2.0.0 - loupe: ^2.3.1 - pathval: ^1.1.1 - type-detect: ^4.0.5 - checksum: acff93fd537f96d4a4d62dd83810285dffcfccb5089e1bf2a1205b28ec82d93dff551368722893cf85004282df10ee68802737c33c90c5493957ed449ed7ce71 - languageName: node - linkType: hard - -"chai@npm:^4.3.7": +"chai@npm:>1.9.0, chai@npm:^4.3.7": version: 4.3.7 resolution: "chai@npm:4.3.7" dependencies: @@ -18414,20 +15757,13 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^3.1.0": +"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 languageName: node linkType: hard -"ci-info@npm:^3.2.0": - version: 3.3.0 - resolution: "ci-info@npm:3.3.0" - checksum: c3d86fe374938ecda5093b1ba39acb535d8309185ba3f23587747c6a057e63f45419b406d880304dbc0e1d72392c9a33e42fe9a1e299209bc0ded5efaa232b66 - languageName: node - linkType: hard - "cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": version: 1.0.4 resolution: "cipher-base@npm:1.0.4" @@ -18832,14 +16168,7 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.10, colorette@npm:^2.0.14, colorette@npm:^2.0.7": - version: 2.0.19 - resolution: "colorette@npm:2.0.19" - checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 - languageName: node - linkType: hard - -"colorette@npm:^2.0.20": +"colorette@npm:^2.0.10, colorette@npm:^2.0.14, colorette@npm:^2.0.20, colorette@npm:^2.0.7": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d @@ -19117,14 +16446,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.4, content-type@npm:~1.0.4": - version: 1.0.4 - resolution: "content-type@npm:1.0.4" - checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0 - languageName: node - linkType: hard - -"content-type@npm:~1.0.5": +"content-type@npm:^1.0.4, content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 @@ -19232,16 +16554,7 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.25.1, core-js-compat@npm:^3.8.1": - version: 3.25.1 - resolution: "core-js-compat@npm:3.25.1" - dependencies: - browserslist: ^4.21.3 - checksum: 34dbec657adc2f660f4cd701709c9c5e27cbd608211c65df09458f80f3e357b9492ba1c5173e17cca72d889dcc6da01268cadf88fb407cf1726e76d301c6143e - languageName: node - linkType: hard - -"core-js-compat@npm:^3.30.1, core-js-compat@npm:^3.30.2": +"core-js-compat@npm:^3.30.1, core-js-compat@npm:^3.30.2, core-js-compat@npm:^3.8.1": version: 3.31.0 resolution: "core-js-compat@npm:3.31.0" dependencies: @@ -19469,7 +16782,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:3.1.5, cross-fetch@npm:^3.1.5": +"cross-fetch@npm:3.1.5": version: 3.1.5 resolution: "cross-fetch@npm:3.1.5" dependencies: @@ -19478,7 +16791,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.0.4": +"cross-fetch@npm:^3.0.4, cross-fetch@npm:^3.1.5": version: 3.1.6 resolution: "cross-fetch@npm:3.1.6" dependencies: @@ -20272,14 +17585,7 @@ __metadata: languageName: node linkType: hard -"decode-uri-component@npm:^0.2.0": - version: 0.2.0 - resolution: "decode-uri-component@npm:0.2.0" - checksum: f3749344ab9305ffcfe4bfe300e2dbb61fc6359e2b736812100a3b1b6db0a5668cba31a05e4b45d4d63dbf1a18dfa354cd3ca5bb3ededddabb8cd293f4404f94 - languageName: node - linkType: hard - -"decode-uri-component@npm:^0.2.2": +"decode-uri-component@npm:^0.2.0, decode-uri-component@npm:^0.2.2": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 @@ -20374,15 +17680,6 @@ __metadata: languageName: node linkType: hard -"deep-eql@npm:^3.0.1": - version: 3.0.1 - resolution: "deep-eql@npm:3.0.1" - dependencies: - type-detect: ^4.0.0 - checksum: 4f4c9fb79eb994fb6e81d4aa8b063adc40c00f831588aa65e20857d5d52f15fb23034a6576ecf886f7ff6222d5ae42e71e9b7d57113e0715b1df7ea1e812b125 - languageName: node - linkType: hard - "deep-eql@npm:^4.1.2": version: 4.1.3 resolution: "deep-eql@npm:4.1.3" @@ -20883,14 +18180,7 @@ __metadata: languageName: node linkType: hard -"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": - version: 2.2.0 - resolution: "domelementtype@npm:2.2.0" - checksum: 24cb386198640cd58aa36f8c987f2ea61859929106d06ffcc8f547e70cb2ed82a6dc56dcb8252b21fba1f1ea07df6e4356d60bfe57f77114ca1aed6828362629 - languageName: node - linkType: hard - -"domelementtype@npm:^2.3.0": +"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": version: 2.3.0 resolution: "domelementtype@npm:2.3.0" checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 @@ -21156,13 +18446,6 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.202": - version: 1.4.249 - resolution: "electron-to-chromium@npm:1.4.249" - checksum: 830a35a157af7ae226f1528d727e369bb13f53bc7a4edefdf718651ace09d7d7b4bd7b70d33b5018b8eff6cf99ee58409b6c4140cd6d56350c1966f280ac5c93 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.4.431": version: 1.4.433 resolution: "electron-to-chromium@npm:1.4.433" @@ -21351,16 +18634,6 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.10.0": - version: 5.10.0 - resolution: "enhanced-resolve@npm:5.10.0" - dependencies: - graceful-fs: ^4.2.4 - tapable: ^2.2.0 - checksum: 0bb9830704db271610f900e8d79d70a740ea16f251263362b0c91af545576d09fe50103496606c1300a05e588372d6f9780a9bc2e30ce8ef9b827ec8f44687ff - languageName: node - linkType: hard - "enhanced-resolve@npm:^5.15.0": version: 5.15.0 resolution: "enhanced-resolve@npm:5.15.0" @@ -21394,20 +18667,13 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0": +"entities@npm:^4.2.0, entities@npm:^4.4.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 languageName: node linkType: hard -"entities@npm:^4.4.0": - version: 4.4.0 - resolution: "entities@npm:4.4.0" - checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2 - languageName: node - linkType: hard - "entities@npm:~2.0.0": version: 2.0.3 resolution: "entities@npm:2.0.3" @@ -21541,13 +18807,6 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^0.9.0": - version: 0.9.3 - resolution: "es-module-lexer@npm:0.9.3" - checksum: 84bbab23c396281db2c906c766af58b1ae2a1a2599844a504df10b9e8dc77ec800b3211fdaa133ff700f5703d791198807bba25d9667392d27a5e9feda344da8 - languageName: node - linkType: hard - "es-module-lexer@npm:^1.2.1": version: 1.3.0 resolution: "es-module-lexer@npm:1.3.0" @@ -21603,7 +18862,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.17.5": +"esbuild@npm:^0.17.5, esbuild@npm:^0.17.6": version: 0.17.19 resolution: "esbuild@npm:0.17.19" dependencies: @@ -21680,83 +18939,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.17.6": - version: 0.17.18 - resolution: "esbuild@npm:0.17.18" - dependencies: - "@esbuild/android-arm": 0.17.18 - "@esbuild/android-arm64": 0.17.18 - "@esbuild/android-x64": 0.17.18 - "@esbuild/darwin-arm64": 0.17.18 - "@esbuild/darwin-x64": 0.17.18 - "@esbuild/freebsd-arm64": 0.17.18 - "@esbuild/freebsd-x64": 0.17.18 - "@esbuild/linux-arm": 0.17.18 - "@esbuild/linux-arm64": 0.17.18 - "@esbuild/linux-ia32": 0.17.18 - "@esbuild/linux-loong64": 0.17.18 - "@esbuild/linux-mips64el": 0.17.18 - "@esbuild/linux-ppc64": 0.17.18 - "@esbuild/linux-riscv64": 0.17.18 - "@esbuild/linux-s390x": 0.17.18 - "@esbuild/linux-x64": 0.17.18 - "@esbuild/netbsd-x64": 0.17.18 - "@esbuild/openbsd-x64": 0.17.18 - "@esbuild/sunos-x64": 0.17.18 - "@esbuild/win32-arm64": 0.17.18 - "@esbuild/win32-ia32": 0.17.18 - "@esbuild/win32-x64": 0.17.18 - dependenciesMeta: - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 900b333f649fd89804216fb61fb5a0ffadc6dc37a2ec3b5981b588f72821676ea649a7c0ec785f0dbe6e774080b084c8af5f6ee7adbc1b138faf2a8c35e2c69c - languageName: node - linkType: hard - "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -22088,14 +19270,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0": - version: 3.4.0 - resolution: "eslint-visitor-keys@npm:3.4.0" - checksum: 33159169462d3989321a1ec1e9aaaf6a24cc403d5d347e9886d1b5bfe18ffa1be73bdc6203143a28a606b142b1af49787f33cff0d6d0813eb5f2e8d2e1a6043c - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^3.4.1": +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1": version: 3.4.1 resolution: "eslint-visitor-keys@npm:3.4.1" checksum: f05121d868202736b97de7d750847a328fcfa8593b031c95ea89425333db59676ac087fa905eba438d0a3c5769632f828187e0c1a0d271832a2153c1d3661c2c @@ -22326,14 +19501,7 @@ __metadata: languageName: node linkType: hard -"eventemitter2@npm:^6.3.1": - version: 6.4.5 - resolution: "eventemitter2@npm:6.4.5" - checksum: 84504f9cf0cc30205cdd46783fe9df3733435e5097f13070b678023110b5ef07847651808ae280cd94c42cd5976880211c7a40321a8ff8fa56f7c5f9c5c11960 - languageName: node - linkType: hard - -"eventemitter2@npm:^6.4.9": +"eventemitter2@npm:^6.3.1, eventemitter2@npm:^6.4.9": version: 6.4.9 resolution: "eventemitter2@npm:6.4.9" checksum: be59577c1e1c35509c7ba0e2624335c35bbcfd9485b8a977384c6cc6759341ea1a98d3cb9dbaa5cea4fff9b687e504504e3f9c2cc1674cf3bd8a43a7c74ea3eb @@ -22738,20 +19906,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": - version: 3.2.11 - resolution: "fast-glob@npm:3.2.11" - dependencies: - "@nodelib/fs.stat": ^2.0.2 - "@nodelib/fs.walk": ^1.2.3 - glob-parent: ^5.1.2 - merge2: ^1.3.0 - micromatch: ^4.0.4 - checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 - languageName: node - linkType: hard - -"fast-glob@npm:^3.2.12": +"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" dependencies: @@ -23320,17 +20475,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9": - version: 1.15.1 - resolution: "follow-redirects@npm:1.15.1" - peerDependenciesMeta: - debug: - optional: true - checksum: 6aa4e3e3cdfa3b9314801a1cd192ba756a53479d9d8cca65bf4db3a3e8834e62139245cd2f9566147c8dfe2efff1700d3e6aefd103de4004a7b99985e71dd533 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.15.2": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.2": version: 1.15.2 resolution: "follow-redirects@npm:1.15.2" peerDependenciesMeta: @@ -23609,13 +20754,6 @@ __metadata: languageName: node linkType: hard -"fs-monkey@npm:^1.0.3": - version: 1.0.3 - resolution: "fs-monkey@npm:1.0.3" - checksum: cf50804833f9b88a476911ae911fe50f61a98d986df52f890bd97e7262796d023698cb2309fa9b74fdd8974f04315b648748a0a8ee059e7d5257b293bfc409c0 - languageName: node - linkType: hard - "fs-monkey@npm:^1.0.4": version: 1.0.4 resolution: "fs-monkey@npm:1.0.4" @@ -24467,14 +21605,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da - languageName: node - linkType: hard - -"graceful-fs@npm:^4.1.5": +"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 @@ -29383,16 +26514,7 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.1.2, memfs@npm:^3.4.3": - version: 3.5.0 - resolution: "memfs@npm:3.5.0" - dependencies: - fs-monkey: ^1.0.3 - checksum: 8427db6c3644eeb9119b7a74b232d9a6178d018878acce6f05bd89d95e28b1073c9eeb00127131b0613b07a003e2e7b15b482f9004e548fe06a0aba7aa02515c - languageName: node - linkType: hard - -"memfs@npm:^3.2.2": +"memfs@npm:^3.1.2, memfs@npm:^3.2.2, memfs@npm:^3.4.3": version: 3.5.3 resolution: "memfs@npm:3.5.3" dependencies: @@ -30232,16 +27354,7 @@ __metadata: languageName: node linkType: hard -"moment-timezone@npm:*, moment-timezone@npm:^0.5.x": - version: 0.5.40 - resolution: "moment-timezone@npm:0.5.40" - dependencies: - moment: ">= 2.9.0" - checksum: 6f6be5412b37fd937bb143efe74bf65b2c3f115fd967a6dc13b717a126ed6dd198bff6db6e179d69a089e20ac03ce7622c6b5598dd585005195554487a91b528 - languageName: node - linkType: hard - -"moment-timezone@npm:^0.5.43, moment-timezone@npm:~0.5.43": +"moment-timezone@npm:*, moment-timezone@npm:^0.5.43, moment-timezone@npm:^0.5.x, moment-timezone@npm:~0.5.43": version: 0.5.43 resolution: "moment-timezone@npm:0.5.43" dependencies: @@ -30250,7 +27363,7 @@ __metadata: languageName: node linkType: hard -"moment@npm:>= 2.9.0, moment@npm:^2.10.2, moment@npm:^2.29.1, moment@npm:^2.29.4": +"moment@npm:^2.10.2, moment@npm:^2.29.1, moment@npm:^2.29.4": version: 2.29.4 resolution: "moment@npm:2.29.4" checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e @@ -30498,16 +27611,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.1, nanoid@npm:^3.3.4": - version: 3.3.4 - resolution: "nanoid@npm:3.3.4" - bin: - nanoid: bin/nanoid.cjs - checksum: 2fddd6dee994b7676f008d3ffa4ab16035a754f4bb586c61df5a22cf8c8c94017aadd360368f47d653829e0569a92b129979152ff97af23a558331e47e37cd9c - languageName: node - linkType: hard - -"nanoid@npm:^3.3.6": +"nanoid@npm:^3.3.1, nanoid@npm:^3.3.6": version: 3.3.6 resolution: "nanoid@npm:3.3.6" bin: @@ -30770,7 +27874,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:2.6.7, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": +"node-fetch@npm:2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" dependencies: @@ -30784,7 +27888,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.11": +"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.11, node-fetch@npm:^2.6.7": version: 2.6.11 resolution: "node-fetch@npm:2.6.11" dependencies: @@ -30928,13 +28032,6 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.6": - version: 2.0.6 - resolution: "node-releases@npm:2.0.6" - checksum: e86a926dc9fbb3b41b4c4a89d998afdf140e20a4e8dbe6c0a807f7b2948b42ea97d7fd3ad4868041487b6e9ee98409829c6e4d84a734a4215dff060a7fbeb4bf - languageName: node - linkType: hard - "node-rsa@npm:^1.1.1": version: 1.1.1 resolution: "node-rsa@npm:1.1.1" @@ -33438,17 +30535,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.0, postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.6": - version: 6.0.10 - resolution: "postcss-selector-parser@npm:6.0.10" - dependencies: - cssesc: ^3.0.0 - util-deprecate: ^1.0.2 - checksum: 46afaa60e3d1998bd7adf6caa374baf857cc58d3ff944e29459c9a9e4680a7fe41597bd5b755fc81d7c388357e9bf67c0251d047c640a09f148e13606b8a8608 - languageName: node - linkType: hard - -"postcss-selector-parser@npm:^6.0.4": +"postcss-selector-parser@npm:^6.0.0, postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6": version: 6.0.13 resolution: "postcss-selector-parser@npm:6.0.13" dependencies: @@ -33549,7 +30636,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.2.15, postcss@npm:~8.4.24": +"postcss@npm:^8.2.15, postcss@npm:^8.3.11, postcss@npm:^8.4.14, postcss@npm:^8.4.23, postcss@npm:~8.4.24": version: 8.4.24 resolution: "postcss@npm:8.4.24" dependencies: @@ -33560,28 +30647,6 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.3.11, postcss@npm:^8.4.14": - version: 8.4.16 - resolution: "postcss@npm:8.4.16" - dependencies: - nanoid: ^3.3.4 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: 10eee25efd77868036403858577da0cefaf2e0905feeaba5770d5438ccdddba3d01cba8063e96b8aac4c6daa0ed413dd5ae0554a433a3c4db38df1d134cffc1f - languageName: node - linkType: hard - -"postcss@npm:^8.4.23": - version: 8.4.23 - resolution: "postcss@npm:8.4.23" - dependencies: - nanoid: ^3.3.6 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: 8bb9d1b2ea6e694f8987d4f18c94617971b2b8d141602725fedcc2222fdc413b776a6e1b969a25d627d7b2681ca5aabb56f59e727ef94072e1b6ac8412105a2f - languageName: node - linkType: hard - "postis@npm:^2.2.0": version: 2.2.0 resolution: "postis@npm:2.2.0" @@ -34151,15 +31216,6 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.10.3": - version: 6.10.3 - resolution: "qs@npm:6.10.3" - dependencies: - side-channel: ^1.0.4 - checksum: 0fac5e6c7191d0295a96d0e83c851aeb015df7e990e4d3b093897d3ac6c94e555dbd0a599739c84d7fa46d7fee282d94ba76943983935cf33bba6769539b8019 - languageName: node - linkType: hard - "qs@npm:6.11.0, qs@npm:^6.10.0, qs@npm:^6.10.3, qs@npm:^6.7.0, qs@npm:^6.9.4, qs@npm:^6.9.6": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -34455,19 +31511,7 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.5.1, raw-body@npm:^2.2.0": - version: 2.5.1 - resolution: "raw-body@npm:2.5.1" - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e - languageName: node - linkType: hard - -"raw-body@npm:2.5.2": +"raw-body@npm:2.5.2, raw-body@npm:^2.2.0": version: 2.5.2 resolution: "raw-body@npm:2.5.2" dependencies: @@ -34491,20 +31535,7 @@ __metadata: languageName: node linkType: hard -"rc-scrollbars@npm:^1.1.5": - version: 1.1.5 - resolution: "rc-scrollbars@npm:1.1.5" - dependencies: - dom-css: ^2.1.0 - raf: ^3.4.1 - peerDependencies: - react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: d2b3710868f70ed7c154768bd03825b543d7804ef1c95086ee9cd851f66a046e1b4dbc9e153f9797e1706a11d263db4a645eff485d16bf1c095754e6e771f5ad - languageName: node - linkType: hard - -"rc-scrollbars@npm:^1.1.6": +"rc-scrollbars@npm:^1.1.5, rc-scrollbars@npm:^1.1.6": version: 1.1.6 resolution: "rc-scrollbars@npm:1.1.6" dependencies: @@ -35988,21 +33019,7 @@ __metadata: languageName: unknown linkType: soft -"rollup@npm:^3.2.5": - version: 3.21.3 - resolution: "rollup@npm:3.21.3" - dependencies: - fsevents: ~2.3.2 - dependenciesMeta: - fsevents: - optional: true - bin: - rollup: dist/bin/rollup - checksum: 430c0c66f1480586ccea77b190524efb6a3af3da5308c7bc77eca3160c3f387c9a56ebb3eef5bfb24f683764bdfdd3dafdea175d215e4b875bbde752619bec12 - languageName: node - linkType: hard - -"rollup@npm:^3.21.0": +"rollup@npm:^3.2.5, rollup@npm:^3.21.0": version: 3.23.0 resolution: "rollup@npm:3.23.0" dependencies: @@ -36310,18 +33327,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": - version: 3.1.1 - resolution: "schema-utils@npm:3.1.1" - dependencies: - "@types/json-schema": ^7.0.8 - ajv: ^6.12.5 - ajv-keywords: ^3.5.2 - checksum: fb73f3d759d43ba033c877628fe9751620a26879f6301d3dbeeb48cf2a65baec5cdf99da65d1bf3b4ff5444b2e59cbe4f81c2456b5e0d2ba7d7fd4aed5da29ce - languageName: node - linkType: hard - -"schema-utils@npm:^3.2.0": +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" dependencies: @@ -36459,18 +33465,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.2, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": - version: 7.3.7 - resolution: "semver@npm:7.3.7" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 - languageName: node - linkType: hard - -"semver@npm:^7.5.2": +"semver@npm:7.x, semver@npm:^7.2, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2": version: 7.5.2 resolution: "semver@npm:7.5.2" dependencies: @@ -36531,7 +33526,7 @@ __metadata: languageName: node linkType: hard -"serialize-javascript@npm:6.0.0, serialize-javascript@npm:^6.0.0": +"serialize-javascript@npm:6.0.0": version: 6.0.0 resolution: "serialize-javascript@npm:6.0.0" dependencies: @@ -38552,28 +35547,6 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.1.3": - version: 5.3.5 - resolution: "terser-webpack-plugin@npm:5.3.5" - dependencies: - "@jridgewell/trace-mapping": ^0.3.14 - jest-worker: ^27.4.5 - schema-utils: ^3.1.1 - serialize-javascript: ^6.0.0 - terser: ^5.14.1 - peerDependencies: - webpack: ^5.1.0 - peerDependenciesMeta: - "@swc/core": - optional: true - esbuild: - optional: true - uglify-js: - optional: true - checksum: 611c7b38d6fa0213dc03f48da9efe29c7edd098fc128a64905f7c9b61af8e7c36c13113d46b50be19ee2b8378442f4e1b8b4ddac9bba2cb73499ed32fc0e18f4 - languageName: node - linkType: hard - "terser@npm:^4.1.2, terser@npm:^4.6.3": version: 4.8.0 resolution: "terser@npm:4.8.0" @@ -38587,7 +35560,7 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.10.0, terser@npm:^5.16.8": +"terser@npm:^5.10.0, terser@npm:^5.16.8, terser@npm:^5.3.4": version: 5.18.0 resolution: "terser@npm:5.18.0" dependencies: @@ -38601,20 +35574,6 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.14.1, terser@npm:^5.3.4": - version: 5.14.2 - resolution: "terser@npm:5.14.2" - dependencies: - "@jridgewell/source-map": ^0.3.2 - acorn: ^8.5.0 - commander: ^2.20.0 - source-map-support: ~0.5.20 - bin: - terser: bin/terser - checksum: cabb50a640d6c2cfb351e4f43dc7bf7436f649755bb83eb78b2cacda426d5e0979bd44e6f92d713f3ca0f0866e322739b9ced888ebbce6508ad872d08de74fcc - languageName: node - linkType: hard - "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -38774,20 +35733,13 @@ __metadata: languageName: node linkType: hard -"tiny-invariant@npm:^1.0.6": +"tiny-invariant@npm:^1.0.6, tiny-invariant@npm:^1.2.0": version: 1.3.1 resolution: "tiny-invariant@npm:1.3.1" checksum: 872dbd1ff20a21303a2fd20ce3a15602cfa7fcf9b228bd694a52e2938224313b5385a1078cb667ed7375d1612194feaca81c4ecbe93121ca1baebe344de4f84c languageName: node linkType: hard -"tiny-invariant@npm:^1.2.0": - version: 1.2.0 - resolution: "tiny-invariant@npm:1.2.0" - checksum: e09a718a7c4a499ba592cdac61f015d87427a0867ca07f50c11fd9b623f90cdba18937b515d4a5e4f43dac92370498d7bdaee0d0e7a377a61095e02c4a92eade - languageName: node - linkType: hard - "tinykeys@npm:^1.4.0": version: 1.4.0 resolution: "tinykeys@npm:1.4.0" @@ -39210,14 +36162,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": - version: 2.5.0 - resolution: "tslib@npm:2.5.0" - checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 - languageName: node - linkType: hard - -"tslib@npm:^2.5.3": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.3": version: 2.5.3 resolution: "tslib@npm:2.5.3" checksum: 88902b309afaf83259131c1e13da1dceb0ad1682a213143a1346a649143924d78cf3760c448b84d796938fd76127183894f8d85cbb3bf9c4fddbfcc140c0003c @@ -39997,20 +36942,6 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.5": - version: 1.0.9 - resolution: "update-browserslist-db@npm:1.0.9" - dependencies: - escalade: ^3.1.1 - picocolors: ^1.0.0 - peerDependencies: - browserslist: ">= 4.21.0" - bin: - browserslist-lint: cli.js - checksum: f625899b236f6a4d7f62b56be1b8da230c5563d1fef84d3ef148f2e1a3f11a5a4b3be4fd7e3703e51274c116194017775b10afb4de09eb2c0d09d36b90f1f578 - languageName: node - linkType: hard - "update-check@npm:1.5.2": version: 1.5.2 resolution: "update-check@npm:1.5.2" @@ -40573,7 +37504,7 @@ __metadata: languageName: node linkType: hard -"vm2@npm:^3.9.19": +"vm2@npm:^3.9.19, vm2@npm:^3.9.8": version: 3.9.19 resolution: "vm2@npm:3.9.19" dependencies: @@ -40585,18 +37516,6 @@ __metadata: languageName: node linkType: hard -"vm2@npm:^3.9.8": - version: 3.9.17 - resolution: "vm2@npm:3.9.17" - dependencies: - acorn: ^8.7.0 - acorn-walk: ^8.2.0 - bin: - vm2: bin/vm2 - checksum: 9a03740a40ab2be5e3348a95fb31512da1a3c85318febb07e5299fa103ff05bcd7b6f458211fa38a1281dc27beccd04ff90355fc1d34fe2ee6ca10d0bb8c6f35 - languageName: node - linkType: hard - "void-elements@npm:3.1.0": version: 3.1.0 resolution: "void-elements@npm:3.1.0" @@ -41030,7 +37949,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:>=4.0.0 <6.0.0": +"webpack@npm:>=4.0.0 <6.0.0, webpack@npm:>=4.43.0 <6.0.0, webpack@npm:^5.9.0": version: 5.88.0 resolution: "webpack@npm:5.88.0" dependencies: @@ -41067,80 +37986,6 @@ __metadata: languageName: node linkType: hard -"webpack@npm:>=4.43.0 <6.0.0": - version: 5.74.0 - resolution: "webpack@npm:5.74.0" - dependencies: - "@types/eslint-scope": ^3.7.3 - "@types/estree": ^0.0.51 - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/wasm-edit": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - acorn: ^8.7.1 - acorn-import-assertions: ^1.7.6 - browserslist: ^4.14.5 - chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.10.0 - es-module-lexer: ^0.9.0 - eslint-scope: 5.1.1 - events: ^3.2.0 - glob-to-regexp: ^0.4.1 - graceful-fs: ^4.2.9 - json-parse-even-better-errors: ^2.3.1 - loader-runner: ^4.2.0 - mime-types: ^2.1.27 - neo-async: ^2.6.2 - schema-utils: ^3.1.0 - tapable: ^2.1.1 - terser-webpack-plugin: ^5.1.3 - watchpack: ^2.4.0 - webpack-sources: ^3.2.3 - peerDependenciesMeta: - webpack-cli: - optional: true - bin: - webpack: bin/webpack.js - checksum: 320c41369a75051b19e18c63f408b3dcc481852e992f83d311771c5ec0f05f2946385e8ebef62030cf3587f0a3d2f12779ffdb191569a966847289ba7313f946 - languageName: node - linkType: hard - -"webpack@npm:^5.9.0": - version: 5.87.0 - resolution: "webpack@npm:5.87.0" - dependencies: - "@types/eslint-scope": ^3.7.3 - "@types/estree": ^1.0.0 - "@webassemblyjs/ast": ^1.11.5 - "@webassemblyjs/wasm-edit": ^1.11.5 - "@webassemblyjs/wasm-parser": ^1.11.5 - acorn: ^8.7.1 - acorn-import-assertions: ^1.9.0 - browserslist: ^4.14.5 - chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.15.0 - es-module-lexer: ^1.2.1 - eslint-scope: 5.1.1 - events: ^3.2.0 - glob-to-regexp: ^0.4.1 - graceful-fs: ^4.2.9 - json-parse-even-better-errors: ^2.3.1 - loader-runner: ^4.2.0 - mime-types: ^2.1.27 - neo-async: ^2.6.2 - schema-utils: ^3.2.0 - tapable: ^2.1.1 - terser-webpack-plugin: ^5.3.7 - watchpack: ^2.4.0 - webpack-sources: ^3.2.3 - peerDependenciesMeta: - webpack-cli: - optional: true - bin: - webpack: bin/webpack.js - checksum: b7d0e390f9d30627e303d54b17cb87b62f49ecffe2d35481f830679904993bae208e23748ffe0e6091b6dd4810562b2f2e88bb0f23b96515d74fb1e3c2898210 - languageName: node - linkType: hard - "websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": version: 0.7.4 resolution: "websocket-driver@npm:0.7.4" @@ -41787,7 +38632,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.0.0, yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": +"yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c @@ -41840,22 +38685,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1": - version: 17.5.1 - resolution: "yargs@npm:17.5.1" - dependencies: - cliui: ^7.0.2 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.3 - y18n: ^5.0.5 - yargs-parser: ^21.0.0 - checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde - languageName: node - linkType: hard - -"yargs@npm:^17.7.1": +"yargs@npm:^17.3.1, yargs@npm:^17.7.1": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From bcd027da67d3a96c8bc8be1f552786cc75dfdafa Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Fri, 7 Jul 2023 14:51:22 -0300 Subject: [PATCH 095/149] test: add unit tests for feature preview hooks (#29755) --- apps/meteor/.mocharc.client.js | 1 + .../client/hooks/useFeaturePreview.spec.tsx | 177 +++++ apps/meteor/client/hooks/useFeaturePreview.ts | 2 +- .../hooks/useFeaturePreviewList.spec.tsx | 89 +++ apps/meteor/jest.client.config.ts | 14 + apps/meteor/package.json | 2 + yarn.lock | 689 +++++++++++++++++- 7 files changed, 972 insertions(+), 2 deletions(-) create mode 100644 apps/meteor/client/hooks/useFeaturePreview.spec.tsx create mode 100644 apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx create mode 100644 apps/meteor/jest.client.config.ts diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 50ef8e213933..7eff846f683c 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -38,4 +38,5 @@ module.exports = { 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts', ], + exclude: ['client/hooks/*.spec.{ts,tsx}'], }; diff --git a/apps/meteor/client/hooks/useFeaturePreview.spec.tsx b/apps/meteor/client/hooks/useFeaturePreview.spec.tsx new file mode 100644 index 000000000000..0d0cbc5eca52 --- /dev/null +++ b/apps/meteor/client/hooks/useFeaturePreview.spec.tsx @@ -0,0 +1,177 @@ +/* eslint-disable react/no-multi-comp */ +import type { ISetting } from '@rocket.chat/core-typings'; +import type { LoginService } from '@rocket.chat/ui-contexts'; +import { SettingsContext, UserContext } from '@rocket.chat/ui-contexts'; +import { renderHook } from '@testing-library/react-hooks'; +import type { ObjectId } from 'mongodb'; +import type { ContextType } from 'react'; +import React from 'react'; + +import { useFeaturePreview } from './useFeaturePreview'; + +const userContextValue: ContextType<typeof UserContext> = { + userId: 'john.doe', + user: { + _id: 'john.doe', + username: 'john.doe', + name: 'John Doe', + createdAt: new Date(), + active: true, + _updatedAt: new Date(), + roles: ['admin'], + type: 'user', + }, + queryPreference: <T,>(pref: string | ObjectId, defaultValue: T) => [ + () => () => undefined, + () => (typeof pref === 'string' ? undefined : defaultValue), + ], + querySubscriptions: () => [() => () => undefined, () => []], + querySubscription: () => [() => () => undefined, () => undefined], + queryRoom: () => [() => () => undefined, () => undefined], + + queryAllServices: () => [() => (): void => undefined, (): LoginService[] => []], + loginWithService: () => () => Promise.reject('loginWithService not implemented'), + loginWithPassword: async () => Promise.reject('loginWithPassword not implemented'), + loginWithToken: async () => Promise.reject('loginWithToken not implemented'), + logout: () => Promise.resolve(), +}; + +const settingContextValue: ContextType<typeof SettingsContext> = { + hasPrivateAccess: true, + isLoading: false, + querySetting: (_id: string) => [() => () => undefined, () => undefined], + querySettings: () => [() => () => undefined, () => []], + dispatch: async () => undefined, +}; + +it('should return false if featurePreviewEnabled is false', () => { + const { result } = renderHook( + () => { + return useFeaturePreview('quickReactions'); + }, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: false, + }} + > + <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(result.all[0]).toBe(false); +}); + +it('should return false if featurePreviewEnabled is true but feature is not in userPreferences', () => { + const { result } = renderHook( + () => { + return useFeaturePreview('quickReactions'); + }, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: false, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: [ + { + name: 'quickReactions', + value: true, + }, + ], + }} + > + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(result.all[0]).toBe(false); +}); + +it('should return true if featurePreviewEnabled is true and feature is in userPreferences', () => { + const { result } = renderHook( + () => { + return useFeaturePreview('quickReactions'); + }, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: [ + { + name: 'quickReactions', + value: true, + }, + ], + }} + > + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(result.all[0]).toBe(true); +}); + +const createUserContextValue = ({ userPreferences }: { userPreferences?: Record<string, unknown> }): ContextType<typeof UserContext> => { + return { + ...userContextValue, + ...(userPreferences && { queryPreference: (id) => [() => () => undefined, () => userPreferences[id as unknown as string] as any] }), + }; +}; + +const createSettingContextValue = ({ settings }: { settings?: Record<string, ISetting['value']> }): ContextType<typeof SettingsContext> => { + const cache = new Map<string, ISetting['value']>(); + + return { + ...settingContextValue, + ...(settings && { + querySetting: (_id: string) => [ + () => () => undefined, + () => { + if (cache.has(_id)) { + return cache.get(_id) as any; + } + cache.set(_id, { value: settings[_id] } as any); + return cache.get(_id) as any; + }, + ], + }), + }; +}; + +export const MockedSettingsContext = ({ + settings, + children, +}: { + children: React.ReactNode; + settings?: Record<string, ISetting['value']>; +}) => { + return <SettingsContext.Provider value={createSettingContextValue({ settings })}>{children}</SettingsContext.Provider>; +}; + +export const MockedUserContext = ({ + userPreferences, + children, +}: { + children: React.ReactNode; + userPreferences?: Record<string, unknown>; +}) => { + return <UserContext.Provider value={createUserContextValue({ userPreferences })}>{children}</UserContext.Provider>; +}; diff --git a/apps/meteor/client/hooks/useFeaturePreview.ts b/apps/meteor/client/hooks/useFeaturePreview.ts index fcfc4ef85c82..2dcc68262b09 100644 --- a/apps/meteor/client/hooks/useFeaturePreview.ts +++ b/apps/meteor/client/hooks/useFeaturePreview.ts @@ -4,6 +4,7 @@ import type { FeaturesAvailable, FeaturePreviewProps } from './useFeaturePreview export const useFeaturePreview = (featureName: FeaturesAvailable) => { const featurePreviewEnabled = useSetting('Accounts_AllowFeaturePreview'); + const features = useUserPreference<FeaturePreviewProps[]>('featuresPreview'); const currentFeature = features?.find((feature) => feature.name === featureName); @@ -13,7 +14,6 @@ export const useFeaturePreview = (featureName: FeaturesAvailable) => { } if (!currentFeature) { - console.error(`Feature ${featureName} not found`); return false; } diff --git a/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx b/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx new file mode 100644 index 000000000000..2fbcdb4cb6d2 --- /dev/null +++ b/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx @@ -0,0 +1,89 @@ +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { MockedSettingsContext, MockedUserContext } from './useFeaturePreview.spec'; +import { useFeaturePreviewList, defaultFeaturesPreview } from './useFeaturePreviewList'; + +it('should return the number of unseen features and Accounts_AllowFeaturePreview enabled ', () => { + const { result } = renderHook( + () => { + return useFeaturePreviewList(); + }, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: defaultFeaturesPreview.length, + }), + ); +}); + +it('should return the number of unseen features and Accounts_AllowFeaturePreview disabled ', () => { + const { result } = renderHook( + () => { + return useFeaturePreviewList(); + }, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: false, + }} + > + <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: false, + unseenFeatures: 0, + }), + ); +}); + +it('should return 0 unseen features', () => { + const { result } = renderHook( + () => { + return useFeaturePreviewList(); + }, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: defaultFeaturesPreview, + }} + > + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: 0, + }), + ); +}); diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.client.config.ts new file mode 100644 index 000000000000..9523d726d9e9 --- /dev/null +++ b/apps/meteor/jest.client.config.ts @@ -0,0 +1,14 @@ +export default { + errorOnDeprecated: true, + + testEnvironment: 'jsdom', + modulePathIgnorePatterns: ['<rootDir>/dist/'], + testMatch: ['<rootDir>/client/hooks/**.spec.[jt]s?(x)', './client/hooks/**.spec.ts', '/client/hooks/**.spec.ts'], + transform: { + '^.+\\.(t|j)sx?$': '@swc/jest', + }, + moduleNameMapper: { + '\\.css$': 'identity-obj-proxy', + '^react($|/.+)': '<rootDir>/node_modules/react$1', + }, +}; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 4f2422cb3fed..0370b5327b4f 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -81,6 +81,7 @@ "@storybook/react": "~6.5.16", "@storybook/testing-library": "0.0.13", "@swc/core": "^1.3.66", + "@swc/jest": "^0.2.26", "@tanstack/react-query-devtools": "^4.19.1", "@testing-library/react": "~12.1.5", "@testing-library/react-hooks": "^8.0.1", @@ -177,6 +178,7 @@ "eslint-plugin-you-dont-need-lodash-underscore": "~6.12.0", "fast-glob": "^3.2.12", "i18next": "^20.6.1", + "jest": "^29.6.1", "jsdom-global": "^3.0.2", "mocha": "^9.2.2", "nyc": "^15.1.0", diff --git a/yarn.lock b/yarn.lock index 539e35e375fa..f855f365525c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3794,6 +3794,20 @@ __metadata: languageName: node linkType: hard +"@jest/console@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/console@npm:29.6.1" + dependencies: + "@jest/types": ^29.6.1 + "@types/node": "*" + chalk: ^4.0.0 + jest-message-util: ^29.6.1 + jest-util: ^29.6.1 + slash: ^3.0.0 + checksum: d0ab23a00947bfb4bff8c0a7e5a7afd16519de16dde3fe7e77b9f13e794c6df7043ecf7fcdde66ac0d2b5fb3262e9cab3d92eaf61f89a12d3b8e3602e06a9902 + languageName: node + linkType: hard + "@jest/core@npm:^29.5.0": version: 29.5.0 resolution: "@jest/core@npm:29.5.0" @@ -3835,6 +3849,47 @@ __metadata: languageName: node linkType: hard +"@jest/core@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/core@npm:29.6.1" + dependencies: + "@jest/console": ^29.6.1 + "@jest/reporters": ^29.6.1 + "@jest/test-result": ^29.6.1 + "@jest/transform": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/node": "*" + ansi-escapes: ^4.2.1 + chalk: ^4.0.0 + ci-info: ^3.2.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + jest-changed-files: ^29.5.0 + jest-config: ^29.6.1 + jest-haste-map: ^29.6.1 + jest-message-util: ^29.6.1 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.6.1 + jest-resolve-dependencies: ^29.6.1 + jest-runner: ^29.6.1 + jest-runtime: ^29.6.1 + jest-snapshot: ^29.6.1 + jest-util: ^29.6.1 + jest-validate: ^29.6.1 + jest-watcher: ^29.6.1 + micromatch: ^4.0.4 + pretty-format: ^29.6.1 + slash: ^3.0.0 + strip-ansi: ^6.0.0 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: 736dcc90c6c58dd9e1d2da122103b851187719ce3b3d4167689c63e68252632cd817712955b52ddaa648eba9c6f98f86cd58677325f0db4185f76899c64d7dac + languageName: node + linkType: hard + "@jest/create-cache-key-function@npm:^27.4.2": version: 27.5.1 resolution: "@jest/create-cache-key-function@npm:27.5.1" @@ -3856,6 +3911,18 @@ __metadata: languageName: node linkType: hard +"@jest/environment@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/environment@npm:29.6.1" + dependencies: + "@jest/fake-timers": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/node": "*" + jest-mock: ^29.6.1 + checksum: fb671f91f27e7aa1ba04983ef87a83f0794a597aba0a57d08cbb1fcb484c2aedc2201e99f85fafe27aec9be78af6f2d1d7e6ea88267938992a1d0f9d4615f5b2 + languageName: node + linkType: hard + "@jest/expect-utils@npm:^29.5.0": version: 29.5.0 resolution: "@jest/expect-utils@npm:29.5.0" @@ -3865,6 +3932,15 @@ __metadata: languageName: node linkType: hard +"@jest/expect-utils@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/expect-utils@npm:29.6.1" + dependencies: + jest-get-type: ^29.4.3 + checksum: 037ee017eca62f7b45e1465fb5c6f9e92d5709a9ac716b8bff0bd294240a54de734e8f968fb69309cc4aef6c83b9552d5a821f3b18371af394bf04783859d706 + languageName: node + linkType: hard + "@jest/expect@npm:^29.5.0": version: 29.5.0 resolution: "@jest/expect@npm:29.5.0" @@ -3875,6 +3951,16 @@ __metadata: languageName: node linkType: hard +"@jest/expect@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/expect@npm:29.6.1" + dependencies: + expect: ^29.6.1 + jest-snapshot: ^29.6.1 + checksum: 5c56977b3cc8489744d97d9dc2dcb196c1dfecc83a058a7ef0fd4f63d68cf120a23d27669272d1e1b184fb4337b85e4ac1fc7f886e3988fdf243d42d73973eac + languageName: node + linkType: hard + "@jest/fake-timers@npm:^29.5.0": version: 29.5.0 resolution: "@jest/fake-timers@npm:29.5.0" @@ -3889,6 +3975,20 @@ __metadata: languageName: node linkType: hard +"@jest/fake-timers@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/fake-timers@npm:29.6.1" + dependencies: + "@jest/types": ^29.6.1 + "@sinonjs/fake-timers": ^10.0.2 + "@types/node": "*" + jest-message-util: ^29.6.1 + jest-mock: ^29.6.1 + jest-util: ^29.6.1 + checksum: 86991276944b7d6c2ada3703a272517f5f8f2f4e2af1fe26065f6db1dac4dc6299729a88c46bcb781dcc1b20504c1d4bbd8119fd8a0838ac81a9a4b5d2c8e429 + languageName: node + linkType: hard + "@jest/globals@npm:^29.5.0": version: 29.5.0 resolution: "@jest/globals@npm:29.5.0" @@ -3901,6 +4001,18 @@ __metadata: languageName: node linkType: hard +"@jest/globals@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/globals@npm:29.6.1" + dependencies: + "@jest/environment": ^29.6.1 + "@jest/expect": ^29.6.1 + "@jest/types": ^29.6.1 + jest-mock: ^29.6.1 + checksum: fcca0b970a8b4894a1cdff0f500a86b45609e72c0a4319875e9504237b839df1a46c44d2f1362c6d87fdc7a05928edcc4b5a3751c9e6648dd70a761cdab64c94 + languageName: node + linkType: hard + "@jest/reporters@npm:^29.5.0": version: 29.5.0 resolution: "@jest/reporters@npm:29.5.0" @@ -3938,6 +4050,43 @@ __metadata: languageName: node linkType: hard +"@jest/reporters@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/reporters@npm:29.6.1" + dependencies: + "@bcoe/v8-coverage": ^0.2.3 + "@jest/console": ^29.6.1 + "@jest/test-result": ^29.6.1 + "@jest/transform": ^29.6.1 + "@jest/types": ^29.6.1 + "@jridgewell/trace-mapping": ^0.3.18 + "@types/node": "*" + chalk: ^4.0.0 + collect-v8-coverage: ^1.0.0 + exit: ^0.1.2 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + istanbul-lib-coverage: ^3.0.0 + istanbul-lib-instrument: ^5.1.0 + istanbul-lib-report: ^3.0.0 + istanbul-lib-source-maps: ^4.0.0 + istanbul-reports: ^3.1.3 + jest-message-util: ^29.6.1 + jest-util: ^29.6.1 + jest-worker: ^29.6.1 + slash: ^3.0.0 + string-length: ^4.0.1 + strip-ansi: ^6.0.0 + v8-to-istanbul: ^9.0.1 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: b7dae415f3f6342b4db2671261bbee29af20a829f42135316c3dd548b9ef85290c9bb64a0e3aec4a55486596be1257ac8216a0f8d9794acd43f8b8fb686fc7e3 + languageName: node + linkType: hard + "@jest/schemas@npm:^28.1.3": version: 28.1.3 resolution: "@jest/schemas@npm:28.1.3" @@ -3956,6 +4105,15 @@ __metadata: languageName: node linkType: hard +"@jest/schemas@npm:^29.6.0": + version: 29.6.0 + resolution: "@jest/schemas@npm:29.6.0" + dependencies: + "@sinclair/typebox": ^0.27.8 + checksum: c00511c69cf89138a7d974404d3a5060af375b5a52b9c87215d91873129b382ca11c1ff25bd6d605951404bb381ddce5f8091004a61e76457da35db1f5c51365 + languageName: node + linkType: hard + "@jest/source-map@npm:^29.4.3": version: 29.4.3 resolution: "@jest/source-map@npm:29.4.3" @@ -3967,6 +4125,17 @@ __metadata: languageName: node linkType: hard +"@jest/source-map@npm:^29.6.0": + version: 29.6.0 + resolution: "@jest/source-map@npm:29.6.0" + dependencies: + "@jridgewell/trace-mapping": ^0.3.18 + callsites: ^3.0.0 + graceful-fs: ^4.2.9 + checksum: 9c6c40387410bb70b2fae8124287fc28f6bdd1b2d7f24348e8611e1bb638b404518228a4ce64a582365b589c536ae8e7ebab0126cef59a87874b71061d19783b + languageName: node + linkType: hard + "@jest/test-result@npm:^29.5.0": version: 29.5.0 resolution: "@jest/test-result@npm:29.5.0" @@ -3979,6 +4148,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-result@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/test-result@npm:29.6.1" + dependencies: + "@jest/console": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/istanbul-lib-coverage": ^2.0.0 + collect-v8-coverage: ^1.0.0 + checksum: 9397a3a3410c5df564e79297b1be4fe33807a6157a017a1f74b54a6ef14de1530f12b922299e822e66a82c53269da16661772bffde3d883a78c5eefd2cd6d1cc + languageName: node + linkType: hard + "@jest/test-sequencer@npm:^29.5.0": version: 29.5.0 resolution: "@jest/test-sequencer@npm:29.5.0" @@ -3991,6 +4172,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-sequencer@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/test-sequencer@npm:29.6.1" + dependencies: + "@jest/test-result": ^29.6.1 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.6.1 + slash: ^3.0.0 + checksum: f3437178b5dca0401ed2e990d8b69161442351856d56f5725e009a487f5232b51039f8829673884b9bea61c861120d08a53a36432f4a4b8aab38915a68f7000d + languageName: node + linkType: hard + "@jest/transform@npm:^26.6.2": version: 26.6.2 resolution: "@jest/transform@npm:26.6.2" @@ -4037,6 +4230,29 @@ __metadata: languageName: node linkType: hard +"@jest/transform@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/transform@npm:29.6.1" + dependencies: + "@babel/core": ^7.11.6 + "@jest/types": ^29.6.1 + "@jridgewell/trace-mapping": ^0.3.18 + babel-plugin-istanbul: ^6.1.1 + chalk: ^4.0.0 + convert-source-map: ^2.0.0 + fast-json-stable-stringify: ^2.1.0 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.6.1 + jest-regex-util: ^29.4.3 + jest-util: ^29.6.1 + micromatch: ^4.0.4 + pirates: ^4.0.4 + slash: ^3.0.0 + write-file-atomic: ^4.0.2 + checksum: 1635cd66e4b3dbba0689ecefabc6137301756c9c12d1d23e25124dd0dd9b4a6a38653d51e825e90f74faa022152ac1eaf200591fb50417aa7e1f7d1d1c2bc11d + languageName: node + linkType: hard + "@jest/types@npm:^26.6.2": version: 26.6.2 resolution: "@jest/types@npm:26.6.2" @@ -4077,6 +4293,20 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/types@npm:29.6.1" + dependencies: + "@jest/schemas": ^29.6.0 + "@types/istanbul-lib-coverage": ^2.0.0 + "@types/istanbul-reports": ^3.0.0 + "@types/node": "*" + "@types/yargs": ^17.0.8 + chalk: ^4.0.0 + checksum: 89fc1ccf71a84fe0da643e0675b1cfe6a6f19ea72e935b2ab1dbdb56ec547e94433fb59b3536d3832a6e156c077865b7176fe9dae707dab9c3d2f9405ba6233c + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2": version: 0.3.2 resolution: "@jridgewell/gen-mapping@npm:0.3.2" @@ -4129,7 +4359,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.18 resolution: "@jridgewell/trace-mapping@npm:0.3.18" dependencies: @@ -7940,6 +8170,7 @@ __metadata: "@storybook/react": ~6.5.16 "@storybook/testing-library": 0.0.13 "@swc/core": ^1.3.66 + "@swc/jest": ^0.2.26 "@tanstack/react-query": ^4.16.1 "@tanstack/react-query-devtools": ^4.19.1 "@testing-library/react": ~12.1.5 @@ -8108,6 +8339,7 @@ __metadata: imap: ^0.8.19 ip-range-check: ^0.2.0 is-svg: ^4.3.2 + jest: ^29.6.1 jquery: ^3.6.0 jschardet: ^3.0.0 jsdom: ^16.7.0 @@ -8974,6 +9206,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 00bd7362a3439021aa1ea51b0e0d0a0e8ca1351a3d54c606b115fdcc49b51b16db6e5f43b4fe7a28c38688523e22a94d49dd31168868b655f0d4d50f032d07a1 + languageName: node + linkType: hard + "@sindresorhus/is@npm:^0.7.0": version: 0.7.0 resolution: "@sindresorhus/is@npm:0.7.0" @@ -13979,6 +14218,23 @@ __metadata: languageName: node linkType: hard +"babel-jest@npm:^29.6.1": + version: 29.6.1 + resolution: "babel-jest@npm:29.6.1" + dependencies: + "@jest/transform": ^29.6.1 + "@types/babel__core": ^7.1.14 + babel-plugin-istanbul: ^6.1.1 + babel-preset-jest: ^29.5.0 + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + slash: ^3.0.0 + peerDependencies: + "@babel/core": ^7.8.0 + checksum: bc46cfba468edde91f34a8292501d4448a39fab72d80d7d95f4349feb114fa21becb01def007d6166de7933ab9633bf5b5e1b72ba6ffeaa991f7abf014a2f61d + languageName: node + linkType: hard + "babel-loader@npm:^8.0.0, babel-loader@npm:^8.3.0": version: 8.3.0 resolution: "babel-loader@npm:8.3.0" @@ -19724,6 +19980,20 @@ __metadata: languageName: node linkType: hard +"expect@npm:^29.6.1": + version: 29.6.1 + resolution: "expect@npm:29.6.1" + dependencies: + "@jest/expect-utils": ^29.6.1 + "@types/node": "*" + jest-get-type: ^29.4.3 + jest-matcher-utils: ^29.6.1 + jest-message-util: ^29.6.1 + jest-util: ^29.6.1 + checksum: 4e712e52c90f6c54e748fd2876be33c43ada6a59088ddf6a1acb08b18b3b97b3a672124684abe32599986d2f2a438d5afad148837ee06ea386d2a4bf0348de78 + languageName: node + linkType: hard + "express-rate-limit@npm:^5.5.1": version: 5.5.1 resolution: "express-rate-limit@npm:5.5.1" @@ -24240,6 +24510,34 @@ __metadata: languageName: node linkType: hard +"jest-circus@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-circus@npm:29.6.1" + dependencies: + "@jest/environment": ^29.6.1 + "@jest/expect": ^29.6.1 + "@jest/test-result": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/node": "*" + chalk: ^4.0.0 + co: ^4.6.0 + dedent: ^0.7.0 + is-generator-fn: ^2.0.0 + jest-each: ^29.6.1 + jest-matcher-utils: ^29.6.1 + jest-message-util: ^29.6.1 + jest-runtime: ^29.6.1 + jest-snapshot: ^29.6.1 + jest-util: ^29.6.1 + p-limit: ^3.1.0 + pretty-format: ^29.6.1 + pure-rand: ^6.0.0 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: f3e39a74b601929448df92037f0599978d4d7a4b8f636f64e8020533d2d2b2f669d6729c80c6efed69341ca26753e5061e9787a0acd6c70af2127a94375ebb76 + languageName: node + linkType: hard + "jest-cli@npm:^29.5.0": version: 29.5.0 resolution: "jest-cli@npm:29.5.0" @@ -24267,6 +24565,33 @@ __metadata: languageName: node linkType: hard +"jest-cli@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-cli@npm:29.6.1" + dependencies: + "@jest/core": ^29.6.1 + "@jest/test-result": ^29.6.1 + "@jest/types": ^29.6.1 + chalk: ^4.0.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + import-local: ^3.0.2 + jest-config: ^29.6.1 + jest-util: ^29.6.1 + jest-validate: ^29.6.1 + prompts: ^2.0.1 + yargs: ^17.3.1 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: f5854ffea977b9a12520ea71f8d0cc8a626cbb93d7e1e6eea18a2a1f2b25f70f1b6b08a89f11b4dc7dd36a1776a9ac2cf8ec5c7998086f913ee690c06c07c949 + languageName: node + linkType: hard + "jest-config@npm:^29.5.0": version: 29.5.0 resolution: "jest-config@npm:29.5.0" @@ -24305,6 +24630,44 @@ __metadata: languageName: node linkType: hard +"jest-config@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-config@npm:29.6.1" + dependencies: + "@babel/core": ^7.11.6 + "@jest/test-sequencer": ^29.6.1 + "@jest/types": ^29.6.1 + babel-jest: ^29.6.1 + chalk: ^4.0.0 + ci-info: ^3.2.0 + deepmerge: ^4.2.2 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + jest-circus: ^29.6.1 + jest-environment-node: ^29.6.1 + jest-get-type: ^29.4.3 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.6.1 + jest-runner: ^29.6.1 + jest-util: ^29.6.1 + jest-validate: ^29.6.1 + micromatch: ^4.0.4 + parse-json: ^5.2.0 + pretty-format: ^29.6.1 + slash: ^3.0.0 + strip-json-comments: ^3.1.1 + peerDependencies: + "@types/node": "*" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + ts-node: + optional: true + checksum: 3a30afeb28cc5658ef9cd95f2551ab8a29641bb6d377eb239cba8e7522eb4611c9a98cdcf173d87f5ad7b5e1ad242c3cd5434a260107bd3c7e8305d05023e05c + languageName: node + linkType: hard + "jest-diff@npm:^27.5.1": version: 27.5.1 resolution: "jest-diff@npm:27.5.1" @@ -24341,6 +24704,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-diff@npm:29.6.1" + dependencies: + chalk: ^4.0.0 + diff-sequences: ^29.4.3 + jest-get-type: ^29.4.3 + pretty-format: ^29.6.1 + checksum: c6350178ca27d92c7fd879790fb2525470c1ff1c5d29b1834a240fecd26c6904fb470ebddb98dc96dd85389c56c3b50e6965a1f5203e9236d213886ed9806219 + languageName: node + linkType: hard + "jest-docblock@npm:^29.4.3": version: 29.4.3 resolution: "jest-docblock@npm:29.4.3" @@ -24363,6 +24738,19 @@ __metadata: languageName: node linkType: hard +"jest-each@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-each@npm:29.6.1" + dependencies: + "@jest/types": ^29.6.1 + chalk: ^4.0.0 + jest-get-type: ^29.4.3 + jest-util: ^29.6.1 + pretty-format: ^29.6.1 + checksum: 9d2ea7ed5326ee8c22523b22c66c85fe73754ea39f9b389881956508ee441392c61072a5fbf673e39beddd31d011bb94acae3edc77053ba4f9aa5c060114a5c8 + languageName: node + linkType: hard + "jest-environment-jsdom@npm:~29.5.0": version: 29.5.0 resolution: "jest-environment-jsdom@npm:29.5.0" @@ -24398,6 +24786,20 @@ __metadata: languageName: node linkType: hard +"jest-environment-node@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-environment-node@npm:29.6.1" + dependencies: + "@jest/environment": ^29.6.1 + "@jest/fake-timers": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/node": "*" + jest-mock: ^29.6.1 + jest-util: ^29.6.1 + checksum: a50287e1ff29d131646bd09acc3222ac6ea0ad61e86bf73851d318ef2be0633a421b8558c4a15ddc67e0ffcfc32da7f6a0d8a2ddbfa85453837899dec88d256c + languageName: node + linkType: hard + "jest-fetch-mock@npm:^3.0.3": version: 3.0.3 resolution: "jest-fetch-mock@npm:3.0.3" @@ -24477,6 +24879,29 @@ __metadata: languageName: node linkType: hard +"jest-haste-map@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-haste-map@npm:29.6.1" + dependencies: + "@jest/types": ^29.6.1 + "@types/graceful-fs": ^4.1.3 + "@types/node": "*" + anymatch: ^3.0.3 + fb-watchman: ^2.0.0 + fsevents: ^2.3.2 + graceful-fs: ^4.2.9 + jest-regex-util: ^29.4.3 + jest-util: ^29.6.1 + jest-worker: ^29.6.1 + micromatch: ^4.0.4 + walker: ^1.0.8 + dependenciesMeta: + fsevents: + optional: true + checksum: 7c74d5a0f6aafa9f4e60fae7949d4774770c0243fb529c24f2f4c81229db479fa318dc8b81e8d226865aef1d600af10bd8404dd208e802318434b46f75d5d869 + languageName: node + linkType: hard + "jest-leak-detector@npm:^29.5.0": version: 29.5.0 resolution: "jest-leak-detector@npm:29.5.0" @@ -24487,6 +24912,16 @@ __metadata: languageName: node linkType: hard +"jest-leak-detector@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-leak-detector@npm:29.6.1" + dependencies: + jest-get-type: ^29.4.3 + pretty-format: ^29.6.1 + checksum: 5122d40c248effaede4c9ee3a99046a3f30088fef7bfc4af534678b432455161399357af46deb6423de7e05c6597920d6ee8cd570e26048886a90d541334f8c8 + languageName: node + linkType: hard + "jest-matcher-utils@npm:^27.0.0": version: 27.5.1 resolution: "jest-matcher-utils@npm:27.5.1" @@ -24511,6 +24946,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-matcher-utils@npm:29.6.1" + dependencies: + chalk: ^4.0.0 + jest-diff: ^29.6.1 + jest-get-type: ^29.4.3 + pretty-format: ^29.6.1 + checksum: d2efa6aed6e4820758b732b9fefd315c7fa4508ee690da656e1c5ac4c1a0f4cee5b04c9719ee1fda9aeb883b4209186c145089ced521e715b9fa70afdfa4a9c6 + languageName: node + linkType: hard + "jest-message-util@npm:^29.5.0": version: 29.5.0 resolution: "jest-message-util@npm:29.5.0" @@ -24528,6 +24975,23 @@ __metadata: languageName: node linkType: hard +"jest-message-util@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-message-util@npm:29.6.1" + dependencies: + "@babel/code-frame": ^7.12.13 + "@jest/types": ^29.6.1 + "@types/stack-utils": ^2.0.0 + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + micromatch: ^4.0.4 + pretty-format: ^29.6.1 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: 3e7cb2ff087fe72255292e151d24e4fbb4cd6134885c0a67a4b302f233fe4110bf7580b176f427f05ad7550eb878ed94237209785d09d659a7d171ffa59c068f + languageName: node + linkType: hard + "jest-mock@npm:^27.0.6": version: 27.5.1 resolution: "jest-mock@npm:27.5.1" @@ -24549,6 +25013,17 @@ __metadata: languageName: node linkType: hard +"jest-mock@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-mock@npm:29.6.1" + dependencies: + "@jest/types": ^29.6.1 + "@types/node": "*" + jest-util: ^29.6.1 + checksum: 5e902f1a7eba1eb1a64eb6c19947fe1316834359d9869d0e2644d8979b9cad0465885dc4c9909c471888cddeea835c938cec6263d386d3d1aad720fc74e52ea1 + languageName: node + linkType: hard + "jest-pnp-resolver@npm:^1.2.2": version: 1.2.2 resolution: "jest-pnp-resolver@npm:1.2.2" @@ -24585,6 +25060,16 @@ __metadata: languageName: node linkType: hard +"jest-resolve-dependencies@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-resolve-dependencies@npm:29.6.1" + dependencies: + jest-regex-util: ^29.4.3 + jest-snapshot: ^29.6.1 + checksum: cee0a0fe53fd4531492a526b6ccd32377baad1eff6e6c124f04e9dc920219fd23fd39be88bb9551ee68d5fe92a3af627b423c9bc65a2aa0ac8a223c0e74dbbbb + languageName: node + linkType: hard + "jest-resolve@npm:^29.5.0": version: 29.5.0 resolution: "jest-resolve@npm:29.5.0" @@ -24602,6 +25087,23 @@ __metadata: languageName: node linkType: hard +"jest-resolve@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-resolve@npm:29.6.1" + dependencies: + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.6.1 + jest-pnp-resolver: ^1.2.2 + jest-util: ^29.6.1 + jest-validate: ^29.6.1 + resolve: ^1.20.0 + resolve.exports: ^2.0.0 + slash: ^3.0.0 + checksum: 9ce979a0f4a751bea58caea76415112df2a3f4d58e294019872244728aadd001f0ec20c873a3c805dd8f7c762143b3c14d00f87d124ed87c9981fbf8723090ef + languageName: node + linkType: hard + "jest-runner@npm:^29.5.0": version: 29.5.0 resolution: "jest-runner@npm:29.5.0" @@ -24631,6 +25133,35 @@ __metadata: languageName: node linkType: hard +"jest-runner@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-runner@npm:29.6.1" + dependencies: + "@jest/console": ^29.6.1 + "@jest/environment": ^29.6.1 + "@jest/test-result": ^29.6.1 + "@jest/transform": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/node": "*" + chalk: ^4.0.0 + emittery: ^0.13.1 + graceful-fs: ^4.2.9 + jest-docblock: ^29.4.3 + jest-environment-node: ^29.6.1 + jest-haste-map: ^29.6.1 + jest-leak-detector: ^29.6.1 + jest-message-util: ^29.6.1 + jest-resolve: ^29.6.1 + jest-runtime: ^29.6.1 + jest-util: ^29.6.1 + jest-watcher: ^29.6.1 + jest-worker: ^29.6.1 + p-limit: ^3.1.0 + source-map-support: 0.5.13 + checksum: 0e4dbda26669ae31fee32f8a62b3119bba510f2d17a098d6157b48a73ed2fc9842405bf893f3045c12b3632c7c0e3399fb22684b18ab5566aff4905b26c79a9a + languageName: node + linkType: hard + "jest-runtime@npm:^29.5.0": version: 29.5.0 resolution: "jest-runtime@npm:29.5.0" @@ -24661,6 +25192,36 @@ __metadata: languageName: node linkType: hard +"jest-runtime@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-runtime@npm:29.6.1" + dependencies: + "@jest/environment": ^29.6.1 + "@jest/fake-timers": ^29.6.1 + "@jest/globals": ^29.6.1 + "@jest/source-map": ^29.6.0 + "@jest/test-result": ^29.6.1 + "@jest/transform": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/node": "*" + chalk: ^4.0.0 + cjs-module-lexer: ^1.0.0 + collect-v8-coverage: ^1.0.0 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.6.1 + jest-message-util: ^29.6.1 + jest-mock: ^29.6.1 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.6.1 + jest-snapshot: ^29.6.1 + jest-util: ^29.6.1 + slash: ^3.0.0 + strip-bom: ^4.0.0 + checksum: 7c360c9694467d996f3d6d914fefa0e7bda554adda8c2b9fba31546dba663d71a64eda103ff68120a2422f3c16db8f0bc2c445923fe8fb934f37e53ef74fb429 + languageName: node + linkType: hard + "jest-serializer@npm:^26.6.2": version: 26.6.2 resolution: "jest-serializer@npm:26.6.2" @@ -24702,6 +25263,35 @@ __metadata: languageName: node linkType: hard +"jest-snapshot@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-snapshot@npm:29.6.1" + dependencies: + "@babel/core": ^7.11.6 + "@babel/generator": ^7.7.2 + "@babel/plugin-syntax-jsx": ^7.7.2 + "@babel/plugin-syntax-typescript": ^7.7.2 + "@babel/types": ^7.3.3 + "@jest/expect-utils": ^29.6.1 + "@jest/transform": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/prettier": ^2.1.5 + babel-preset-current-node-syntax: ^1.0.0 + chalk: ^4.0.0 + expect: ^29.6.1 + graceful-fs: ^4.2.9 + jest-diff: ^29.6.1 + jest-get-type: ^29.4.3 + jest-matcher-utils: ^29.6.1 + jest-message-util: ^29.6.1 + jest-util: ^29.6.1 + natural-compare: ^1.4.0 + pretty-format: ^29.6.1 + semver: ^7.5.3 + checksum: e8f69d1fd4a29d354d4dca9eb2a22674b300f8ef509e4f1e75337c880414a00d2bdc9d3849a6855dbb5a76bfbe74603f33435378a3877e69f0838e4cc2244350 + languageName: node + linkType: hard + "jest-util@npm:^26.6.2": version: 26.6.2 resolution: "jest-util@npm:26.6.2" @@ -24730,6 +25320,20 @@ __metadata: languageName: node linkType: hard +"jest-util@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-util@npm:29.6.1" + dependencies: + "@jest/types": ^29.6.1 + "@types/node": "*" + chalk: ^4.0.0 + ci-info: ^3.2.0 + graceful-fs: ^4.2.9 + picomatch: ^2.2.3 + checksum: fc553556c1350c443449cadaba5fb9d604628e8b5ceb6ceaf4e7e08975b24277d0a14bf2e0f956024e03c23e556fcb074659423422a06fbedf2ab52978697ac7 + languageName: node + linkType: hard + "jest-validate@npm:^29.5.0": version: 29.5.0 resolution: "jest-validate@npm:29.5.0" @@ -24744,6 +25348,20 @@ __metadata: languageName: node linkType: hard +"jest-validate@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-validate@npm:29.6.1" + dependencies: + "@jest/types": ^29.6.1 + camelcase: ^6.2.0 + chalk: ^4.0.0 + jest-get-type: ^29.4.3 + leven: ^3.1.0 + pretty-format: ^29.6.1 + checksum: d2491f3f33d9bbc2dcaaa6acbff26f257b59c5eeceb65a52a9c1cec2f679b836ec2a4658b7004c0ef9d90cd0d9bd664e41d5ed6900f932bea742dd8e6b85e7f1 + languageName: node + linkType: hard + "jest-watcher@npm:^29.5.0": version: 29.5.0 resolution: "jest-watcher@npm:29.5.0" @@ -24760,6 +25378,22 @@ __metadata: languageName: node linkType: hard +"jest-watcher@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-watcher@npm:29.6.1" + dependencies: + "@jest/test-result": ^29.6.1 + "@jest/types": ^29.6.1 + "@types/node": "*" + ansi-escapes: ^4.2.1 + chalk: ^4.0.0 + emittery: ^0.13.1 + jest-util: ^29.6.1 + string-length: ^4.0.1 + checksum: 69bd5a602284fdce6eba5486c5c57aca6b511d91cb0907c34c104d6dd931e18ce67baa7f8e280fa473e5d81ea3e7b9e7d94f712c37ab0b3b8cc2aec30676955d + languageName: node + linkType: hard + "jest-websocket-mock@npm:^2.4.0": version: 2.4.0 resolution: "jest-websocket-mock@npm:2.4.0" @@ -24804,6 +25438,18 @@ __metadata: languageName: node linkType: hard +"jest-worker@npm:^29.6.1": + version: 29.6.1 + resolution: "jest-worker@npm:29.6.1" + dependencies: + "@types/node": "*" + jest-util: ^29.6.1 + merge-stream: ^2.0.0 + supports-color: ^8.0.0 + checksum: 0af309ea4db17c4c47e84a9246f907960a15577683c005fdeafc8f3c06bc455136f95a6f28fa2a3e924b767eb4dacd9b40915a7707305f88586f099af3ac27a8 + languageName: node + linkType: hard + "jest@npm:^29.5.0, jest@npm:~29.5.0": version: 29.5.0 resolution: "jest@npm:29.5.0" @@ -24823,6 +25469,25 @@ __metadata: languageName: node linkType: hard +"jest@npm:^29.6.1": + version: 29.6.1 + resolution: "jest@npm:29.6.1" + dependencies: + "@jest/core": ^29.6.1 + "@jest/types": ^29.6.1 + import-local: ^3.0.2 + jest-cli: ^29.6.1 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 7b8c0ca72f483e00ec19dcf9549f9a9af8ae468ab62925b148d714b58eb52d5fea9a082625193bc833d2d9b64cf65a11f3d37857636c5551af05c10aec4ce71b + languageName: node + linkType: hard + "jmespath@npm:0.16.0": version: 0.16.0 resolution: "jmespath@npm:0.16.0" @@ -30834,6 +31499,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.6.1": + version: 29.6.1 + resolution: "pretty-format@npm:29.6.1" + dependencies: + "@jest/schemas": ^29.6.0 + ansi-styles: ^5.0.0 + react-is: ^18.0.0 + checksum: 6f923a2379a37a425241dc223d76f671c73c4f37dba158050575a54095867d565c068b441843afdf3d7c37bed9df4bbadf46297976e60d4149972b779474203a + languageName: node + linkType: hard + "pretty-hrtime@npm:^1.0.3": version: 1.0.3 resolution: "pretty-hrtime@npm:1.0.3" @@ -33476,6 +34152,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.3": + version: 7.5.3 + resolution: "semver@npm:7.5.3" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 9d58db16525e9f749ad0a696a1f27deabaa51f66e91d2fa2b0db3de3e9644e8677de3b7d7a03f4c15bc81521e0c3916d7369e0572dbde250d9bedf5194e2a8a7 + languageName: node + linkType: hard + "semver@npm:~5.3.0": version: 5.3.0 resolution: "semver@npm:5.3.0" From 7f54572d2cc83a8a184c3821d8d64f867a48d694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:50:11 -0300 Subject: [PATCH 096/149] regression: fix condition to `GenericMenu` gap (#29761) --- .../components/GenericMenu/GenericMenu.tsx | 3 +- yarn.lock | 3572 ++++++++++++++++- 2 files changed, 3373 insertions(+), 202 deletions(-) diff --git a/apps/meteor/client/components/GenericMenu/GenericMenu.tsx b/apps/meteor/client/components/GenericMenu/GenericMenu.tsx index aba2e7e0eefb..e02d6dc4e746 100644 --- a/apps/meteor/client/components/GenericMenu/GenericMenu.tsx +++ b/apps/meteor/client/components/GenericMenu/GenericMenu.tsx @@ -40,7 +40,8 @@ const GenericMenu = ({ title, icon = 'menu', onAction, ...props }: GenericMenuPr const handleAction = useHandleMenuAction(itemsList || []); const hasIcon = itemsList.some(({ icon }) => icon); - const handleItems = (items: GenericMenuItemProps[]) => (hasIcon ? items.map((item) => ({ ...item, gap: !item.icon })) : items); + const handleItems = (items: GenericMenuItemProps[]) => + hasIcon ? items.map((item) => ({ ...item, gap: !item.icon && !item.status })) : items; return ( <> diff --git a/yarn.lock b/yarn.lock index f855f365525c..3dcf48ca6520 100644 --- a/yarn.lock +++ b/yarn.lock @@ -957,7 +957,16 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.5, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": + version: 7.21.4 + resolution: "@babel/code-frame@npm:7.21.4" + dependencies: + "@babel/highlight": ^7.18.6 + checksum: e5390e6ec1ac58dcef01d4f18eaf1fd2f1325528661ff6d4a5de8979588b9f5a8e852a54a91b923846f7a5c681b217f0a45c2524eb9560553160cd963b7d592c + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.22.5": version: 7.22.5 resolution: "@babel/code-frame@npm:7.22.5" dependencies: @@ -966,7 +975,21 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.5": +"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/compat-data@npm:7.21.4" + checksum: 5f8b98c66f2ffba9f3c3a82c0cf354c52a0ec5ad4797b370dc32bdcd6e136ac4febe5e93d76ce76e175632e2dbf6ce9f46319aa689fcfafa41b6e49834fa4b66 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.21.5": + version: 7.21.7 + resolution: "@babel/compat-data@npm:7.21.7" + checksum: 28747eb3fc084d088ba2db0336f52118cfa730a57bdbac81630cae1f38ad0336605b95b3390325937802f344e0b7fa25e2f1b67e3ee2d7383b877f88dee0e51c + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.22.5": version: 7.22.5 resolution: "@babel/compat-data@npm:7.22.5" checksum: eb1a47ebf79ae268b4a16903e977be52629339806e248455eb9973897c503a04b701f36a9de64e19750d6e081d0561e77a514c8dc470babbeba59ae94298ed18 @@ -997,7 +1020,30 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.1.0, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.20.5, @babel/core@npm:^7.21.4, @babel/core@npm:^7.7.5, @babel/core@npm:~7.22.5": +"@babel/core@npm:^7.1.0, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.7.5": + version: 7.21.4 + resolution: "@babel/core@npm:7.21.4" + dependencies: + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.21.4 + "@babel/generator": ^7.21.4 + "@babel/helper-compilation-targets": ^7.21.4 + "@babel/helper-module-transforms": ^7.21.2 + "@babel/helpers": ^7.21.0 + "@babel/parser": ^7.21.4 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.4 + "@babel/types": ^7.21.4 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.2 + semver: ^6.3.0 + checksum: a3beebb2cc79908a02f27a07dc381bcb34e8ecc58fa99f568ad0934c49e12111fc977ee9c5b51eb7ea2da66f63155d37c4dd96b6472eaeecfc35843ccb56bf3d + languageName: node + linkType: hard + +"@babel/core@npm:^7.20.5, @babel/core@npm:~7.22.5": version: 7.22.5 resolution: "@babel/core@npm:7.22.5" dependencies: @@ -1020,6 +1066,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.21.4": + version: 7.21.8 + resolution: "@babel/core@npm:7.21.8" + dependencies: + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.21.4 + "@babel/generator": ^7.21.5 + "@babel/helper-compilation-targets": ^7.21.5 + "@babel/helper-module-transforms": ^7.21.5 + "@babel/helpers": ^7.21.5 + "@babel/parser": ^7.21.8 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.5 + "@babel/types": ^7.21.5 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.2 + semver: ^6.3.0 + checksum: f28118447355af2a90bd340e2e60699f94c8020517eba9b71bf8ebff62fa9e00d63f076e033f9dfb97548053ad62ada45fafb0d96584b1a90e8aef5a3b8241b1 + languageName: node + linkType: hard + "@babel/eslint-parser@npm:^7.22.5, @babel/eslint-parser@npm:~7.22.5": version: 7.22.5 resolution: "@babel/eslint-parser@npm:7.22.5" @@ -1034,7 +1103,31 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.22.5, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.21.4, @babel/generator@npm:^7.7.2": + version: 7.21.4 + resolution: "@babel/generator@npm:7.21.4" + dependencies: + "@babel/types": ^7.21.4 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: 9ffbb526a53bb8469b5402f7b5feac93809b09b2a9f82fcbfcdc5916268a65dae746a1f2479e03ba4fb0776facd7c892191f63baa61ab69b2cfdb24f7b92424d + languageName: node + linkType: hard + +"@babel/generator@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/generator@npm:7.21.5" + dependencies: + "@babel/types": ^7.21.5 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: 78af737b9dd701d4c657f9731880430fa1c177767b562f4e8a330a7fe72a4abe857e3d24de4e6d9dafc1f6a11f894162d27e523d7e5948ff9e3925a0ce9867c4 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/generator@npm:7.22.5" dependencies: @@ -1046,7 +1139,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.18.6, @babel/helper-annotate-as-pure@npm:^7.22.5": +"@babel/helper-annotate-as-pure@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: 88ccd15ced475ef2243fdd3b2916a29ea54c5db3cd0cfabf9d1d29ff6e63b7f7cd1c27264137d7a40ac2e978b9b9a542c332e78f40eb72abe737a7400788fc1b + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" dependencies: @@ -1055,6 +1157,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.18.6" + dependencies: + "@babel/helper-explode-assignable-expression": ^7.18.6 + "@babel/types": ^7.18.6 + checksum: c4d71356e0adbc20ce9fe7c1e1181ff65a78603f8bba7615745f0417fed86bad7dc0a54a840bc83667c66709b3cb3721edcb9be0d393a298ce4e9eb6d085f3c1 + languageName: node + linkType: hard + "@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.5" @@ -1064,7 +1176,37 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.5": +"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/helper-compilation-targets@npm:7.21.4" + dependencies: + "@babel/compat-data": ^7.21.4 + "@babel/helper-validator-option": ^7.21.0 + browserslist: ^4.21.3 + lru-cache: ^5.1.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: bf9c7d3e7e6adff9222c05d898724cd4ee91d7eb9d52222c7ad2a22955620c2872cc2d9bdf0e047df8efdb79f4e3af2a06b53f509286145feccc4d10ddc318be + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helper-compilation-targets@npm:7.21.5" + dependencies: + "@babel/compat-data": ^7.21.5 + "@babel/helper-validator-option": ^7.21.0 + browserslist: ^4.21.3 + lru-cache: ^5.1.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 0edecb9c970ddc22ebda1163e77a7f314121bef9e483e0e0d9a5802540eed90d5855b6bf9bce03419b35b2e07c323e62d0353b153fa1ca34f17dbba897a83c25 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-compilation-targets@npm:7.22.5" dependencies: @@ -1079,7 +1221,44 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.17.6, @babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.22.5": +"@babel/helper-create-class-features-plugin@npm:^7.17.6, @babel/helper-create-class-features-plugin@npm:^7.18.6": + version: 7.21.8 + resolution: "@babel/helper-create-class-features-plugin@npm:7.21.8" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.21.5 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-member-expression-to-functions": ^7.21.5 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-replace-supers": ^7.21.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/helper-split-export-declaration": ^7.18.6 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 26b978bd2e741259c0f4a1cc37521ad58728c50d28fe2fc8041d4381497e13a0b686a10e170246855eaf3af08886862e9d93fc27994ef914e13fca0d73efdcb8 + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.21.0": + version: 7.21.4 + resolution: "@babel/helper-create-class-features-plugin@npm:7.21.4" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-member-expression-to-functions": ^7.21.0 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-replace-supers": ^7.20.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/helper-split-export-declaration": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 9123ca80a4894aafdb1f0bc08e44f6be7b12ed1fbbe99c501b484f9b1a17ff296b6c90c18c222047d53c276f07f17b4de857946fa9d0aa207023b03e4cc716f2 + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-create-class-features-plugin@npm:7.22.5" dependencies: @@ -1098,7 +1277,32 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6": + version: 7.21.8 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.8" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + regexpu-core: ^5.3.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 04a686b5897c86339395894c0a9a1ffdce2facaba5173ce7b0a894f775f984ba70d2fa227d309f2be54f7f1286ebd1a0a7051a8b1829521595e4064ee062af65 + languageName: node + linkType: hard + +"@babel/helper-create-regexp-features-plugin@npm:^7.20.5": + version: 7.21.4 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.4" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + regexpu-core: ^5.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 78334865db2cd1d64d103bd0d96dee2818b0387d10aa973c084e245e829df32652bca530803e397b7158af4c02b9b21d5a9601c29bdfbb8d54a3d4ad894e067b + languageName: node + linkType: hard + +"@babel/helper-create-regexp-features-plugin@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.5" dependencies: @@ -1129,6 +1333,22 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.3.3": + version: 0.3.3 + resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" + dependencies: + "@babel/helper-compilation-targets": ^7.17.7 + "@babel/helper-plugin-utils": ^7.16.7 + debug: ^4.1.1 + lodash.debounce: ^4.0.8 + resolve: ^1.14.2 + semver: ^6.1.2 + peerDependencies: + "@babel/core": ^7.4.0-0 + checksum: 8e3fe75513302e34f6d92bd67b53890e8545e6c5bca8fe757b9979f09d68d7e259f6daea90dc9e01e332c4f8781bda31c5fe551c82a277f9bc0bec007aed497c + languageName: node + linkType: hard + "@babel/helper-define-polyfill-provider@npm:^0.4.0": version: 0.4.0 resolution: "@babel/helper-define-polyfill-provider@npm:0.4.0" @@ -1145,6 +1365,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.18.9, @babel/helper-environment-visitor@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helper-environment-visitor@npm:7.21.5" + checksum: e436af7b62956e919066448013a3f7e2cd0b51010c26c50f790124dcd350be81d5597b4e6ed0a4a42d098a27de1e38561cd7998a116a42e7899161192deac9a6 + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-environment-visitor@npm:7.22.5" @@ -1152,6 +1379,25 @@ __metadata: languageName: node linkType: hard +"@babel/helper-explode-assignable-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: 225cfcc3376a8799023d15dc95000609e9d4e7547b29528c7f7111a0e05493ffb12c15d70d379a0bb32d42752f340233c4115bded6d299bc0c3ab7a12be3d30f + languageName: node + linkType: hard + +"@babel/helper-function-name@npm:^7.18.9, @babel/helper-function-name@npm:^7.19.0, @babel/helper-function-name@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-function-name@npm:7.21.0" + dependencies: + "@babel/template": ^7.20.7 + "@babel/types": ^7.21.0 + checksum: d63e63c3e0e3e8b3138fa47b0cd321148a300ef12b8ee951196994dcd2a492cc708aeda94c2c53759a5c9177fffaac0fd8778791286746f72a000976968daf4e + languageName: node + linkType: hard + "@babel/helper-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-function-name@npm:7.22.5" @@ -1162,6 +1408,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-hoist-variables@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-hoist-variables@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f + languageName: node + linkType: hard + "@babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" @@ -1171,6 +1426,24 @@ __metadata: languageName: node linkType: hard +"@babel/helper-member-expression-to-functions@npm:^7.20.7, @babel/helper-member-expression-to-functions@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-member-expression-to-functions@npm:7.21.0" + dependencies: + "@babel/types": ^7.21.0 + checksum: 49cbb865098195fe82ba22da3a8fe630cde30dcd8ebf8ad5f9a24a2b685150c6711419879cf9d99b94dad24cff9244d8c2a890d3d7ec75502cd01fe58cff5b5d + languageName: node + linkType: hard + +"@babel/helper-member-expression-to-functions@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helper-member-expression-to-functions@npm:7.21.5" + dependencies: + "@babel/types": ^7.21.5 + checksum: c404b4a0271c640b7dc8c34af7b683c70a43200259e02330cfc02e79e6b271e9227f35554cd6ad015eabcfa1fea75b9d0b87b69f3d1e6c2af6edd224060b1732 + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-member-expression-to-functions@npm:7.22.5" @@ -1180,7 +1453,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.22.5": +"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/helper-module-imports@npm:7.21.4" + dependencies: + "@babel/types": ^7.21.4 + checksum: bd330a2edaafeb281fbcd9357652f8d2666502567c0aad71db926e8499c773c9ea9c10dfaae30122452940326d90c8caff5c649ed8e1bf15b23f858758d3abc6 + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-imports@npm:7.22.5" dependencies: @@ -1189,7 +1471,39 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.22.5": +"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helper-module-transforms@npm:7.21.5" + dependencies: + "@babel/helper-environment-visitor": ^7.21.5 + "@babel/helper-module-imports": ^7.21.4 + "@babel/helper-simple-access": ^7.21.5 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/helper-validator-identifier": ^7.19.1 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.5 + "@babel/types": ^7.21.5 + checksum: 1ccfc88830675a5d485d198e918498f9683cdd46f973fdd4fe1c85b99648fb70f87fca07756c7a05dc201bd9b248c74ced06ea80c9991926ac889f53c3659675 + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.2": + version: 7.21.2 + resolution: "@babel/helper-module-transforms@npm:7.21.2" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-simple-access": ^7.20.2 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/helper-validator-identifier": ^7.19.1 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.2 + "@babel/types": ^7.21.2 + checksum: 8a1c129a4f90bdf97d8b6e7861732c9580f48f877aaaafbc376ce2482febebcb8daaa1de8bc91676d12886487603f8c62a44f9e90ee76d6cac7f9225b26a49e1 + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-transforms@npm:7.22.5" dependencies: @@ -1205,6 +1519,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-optimise-call-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: e518fe8418571405e21644cfb39cf694f30b6c47b10b006609a92469ae8b8775cbff56f0b19732343e2ea910641091c5a2dc73b56ceba04e116a33b0f8bd2fbd + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" @@ -1221,13 +1544,34 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.21.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.21.5 + resolution: "@babel/helper-plugin-utils@npm:7.21.5" + checksum: 6f086e9a84a50ea7df0d5639c8f9f68505af510ea3258b3c8ac8b175efdfb7f664436cb48996f71791a1350ba68f47ad3424131e8e718c5e2ad45564484cbb36 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-plugin-utils@npm:7.22.5" checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 languageName: node linkType: hard +"@babel/helper-remap-async-to-generator@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-wrap-function": ^7.18.9 + "@babel/types": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 4be6076192308671b046245899b703ba090dbe7ad03e0bea897bb2944ae5b88e5e85853c9d1f83f643474b54c578d8ac0800b80341a86e8538264a725fbbefec + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-remap-async-to-generator@npm:7.22.5" @@ -1242,7 +1586,35 @@ __metadata: languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.22.5": +"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.18.6, @babel/helper-replace-supers@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helper-replace-supers@npm:7.21.5" + dependencies: + "@babel/helper-environment-visitor": ^7.21.5 + "@babel/helper-member-expression-to-functions": ^7.21.5 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.5 + "@babel/types": ^7.21.5 + checksum: 4fd343e6f90533743d8e8a1f42e50377b3d6b27f524a27eb97ff28f075e4e55cca2383adb1b0973de358b08022aef0fec4c8d69711e1da43bf9b887b5a893677 + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/helper-replace-supers@npm:7.20.7" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-member-expression-to-functions": ^7.20.7 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.20.7 + "@babel/types": ^7.20.7 + checksum: b8e0087c9b0c1446e3c6f3f72b73b7e03559c6b570e2cfbe62c738676d9ebd8c369a708cf1a564ef88113b4330750a50232ee1131d303d478b7a5e65e46fbc7c + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-replace-supers@npm:7.22.5" dependencies: @@ -1256,6 +1628,24 @@ __metadata: languageName: node linkType: hard +"@babel/helper-simple-access@npm:^7.20.2": + version: 7.20.2 + resolution: "@babel/helper-simple-access@npm:7.20.2" + dependencies: + "@babel/types": ^7.21.5 + checksum: ad1e96ee2e5f654ffee2369a586e5e8d2722bf2d8b028a121b4c33ebae47253f64d420157b9f0a8927aea3a9e0f18c0103e74fdd531815cf3650a0a4adca11a1 + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helper-simple-access@npm:7.21.5" + dependencies: + "@babel/types": ^7.21.5 + checksum: ad212beaa24be3864c8c95bee02f840222457ccf5419991e2d3e3e39b0f75b77e7e857e0bf4ed428b1cd97acefc87f3831bdb0b9696d5ad0557421f398334fc3 + languageName: node + linkType: hard + "@babel/helper-simple-access@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-simple-access@npm:7.22.5" @@ -1265,7 +1655,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0, @babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0": + version: 7.20.0 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.20.0" + dependencies: + "@babel/types": ^7.20.0 + checksum: 34da8c832d1c8a546e45d5c1d59755459ffe43629436707079989599b91e8c19e50e73af7a4bd09c95402d389266731b0d9c5f69e372d8ebd3a709c05c80d7dd + languageName: node + linkType: hard + +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" dependencies: @@ -1274,6 +1673,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-split-export-declaration@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-split-export-declaration@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: c6d3dede53878f6be1d869e03e9ffbbb36f4897c7cc1527dc96c56d127d834ffe4520a6f7e467f5b6f3c2843ea0e81a7819d66ae02f707f6ac057f3d57943a2b + languageName: node + linkType: hard + "@babel/helper-split-export-declaration@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-split-export-declaration@npm:7.22.5" @@ -1283,13 +1691,27 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.22.5": +"@babel/helper-string-parser@npm:^7.19.4, @babel/helper-string-parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-string-parser@npm:7.22.5" checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helper-string-parser@npm:7.21.5" + checksum: 36c0ded452f3858e67634b81960d4bde1d1cd2a56b82f4ba2926e97864816021c885f111a7cf81de88a0ed025f49d84a393256700e9acbca2d99462d648705d8 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": + version: 7.19.1 + resolution: "@babel/helper-validator-identifier@npm:7.19.1" + checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-identifier@npm:7.22.5" @@ -1297,13 +1719,32 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.22.5": +"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.18.6, @babel/helper-validator-option@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-validator-option@npm:7.21.0" + checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-option@npm:7.22.5" checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 languageName: node linkType: hard +"@babel/helper-wrap-function@npm:^7.18.9": + version: 7.19.0 + resolution: "@babel/helper-wrap-function@npm:7.19.0" + dependencies: + "@babel/helper-function-name": ^7.19.0 + "@babel/template": ^7.18.10 + "@babel/traverse": ^7.19.0 + "@babel/types": ^7.19.0 + checksum: 2453a6b134f12cc779179188c4358a66252c29b634a8195c0cf626e17f9806c3c4c40e159cd8056c2ec82b69b9056a088014fa43d6ccc1aca67da8d9605da8fd + languageName: node + linkType: hard + "@babel/helper-wrap-function@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-wrap-function@npm:7.22.5" @@ -1316,7 +1757,29 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.22.5": +"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helpers@npm:7.21.0" + dependencies: + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.0 + "@babel/types": ^7.21.0 + checksum: 9370dad2bb665c551869a08ac87c8bdafad53dbcdce1f5c5d498f51811456a3c005d9857562715151a0f00b2e912ac8d89f56574f837b5689f5f5072221cdf54 + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/helpers@npm:7.21.5" + dependencies: + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.5 + "@babel/types": ^7.21.5 + checksum: a6f74b8579713988e7f5adf1a986d8b5255757632ba65b2552f0f609ead5476edb784044c7e4b18f3681ee4818ca9d08c41feb9bd4e828648c25a00deaa1f9e4 + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helpers@npm:7.22.5" dependencies: @@ -1327,7 +1790,18 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.22.5": +"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/highlight@npm:7.18.6" + dependencies: + "@babel/helper-validator-identifier": ^7.18.6 + chalk: ^2.0.0 + js-tokens: ^4.0.0 + checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.22.5": version: 7.22.5 resolution: "@babel/highlight@npm:7.22.5" dependencies: @@ -1338,7 +1812,25 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/parser@npm:7.21.4" + bin: + parser: ./bin/babel-parser.js + checksum: de610ecd1bff331766d0c058023ca11a4f242bfafefc42caf926becccfb6756637d167c001987ca830dd4b34b93c629a4cef63f8c8c864a8564cdfde1989ac77 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.21.5, @babel/parser@npm:^7.21.8": + version: 7.21.8 + resolution: "@babel/parser@npm:7.21.8" + bin: + parser: ./bin/babel-parser.js + checksum: 1b9a820fedfb6ef179e6ffa1dbc080808882949dec68340a616da2aa354af66ea2886bd68e61bd444d270aa0b24ad6273e3cfaf17d6878c34bf2521becacb353 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/parser@npm:7.22.5" bin: @@ -1347,6 +1839,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 845bd280c55a6a91d232cfa54eaf9708ec71e594676fe705794f494bb8b711d833b752b59d1a5c154695225880c23dbc9cab0e53af16fd57807976cd3ff41b8d + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.5" @@ -1358,6 +1861,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/plugin-proposal-optional-chaining": ^7.20.7 + peerDependencies: + "@babel/core": ^7.13.0 + checksum: d610f532210bee5342f5b44a12395ccc6d904e675a297189bc1e401cc185beec09873da523466d7fec34ae1574f7a384235cba1ccc9fe7b89ba094167897c845 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.5" @@ -1371,7 +1887,21 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.12.1": +"@babel/plugin-proposal-async-generator-functions@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 + languageName: node + linkType: hard + +"@babel/plugin-proposal-class-properties@npm:^7.12.1, @babel/plugin-proposal-class-properties@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: @@ -1383,6 +1913,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-class-static-block@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 236c0ad089e7a7acab776cc1d355330193314bfcd62e94e78f2df35817c6144d7e0e0368976778afd6b7c13e70b5068fa84d7abbf967d4f182e60d03f9ef802b + languageName: node + linkType: hard + "@babel/plugin-proposal-decorators@npm:^7.12.12": version: 7.17.8 resolution: "@babel/plugin-proposal-decorators@npm:7.17.8" @@ -1398,6 +1941,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f + languageName: node + linkType: hard + "@babel/plugin-proposal-export-default-from@npm:^7.12.1": version: 7.16.7 resolution: "@babel/plugin-proposal-export-default-from@npm:7.16.7" @@ -1410,6 +1965,42 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef + languageName: node + linkType: hard + +"@babel/plugin-proposal-json-strings@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-json-strings": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 + languageName: node + linkType: hard + +"@babel/plugin-proposal-logical-assignment-operators@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cdd7b8136cc4db3f47714d5266f9e7b592a2ac5a94a5878787ce08890e97c8ab1ca8e94b27bfeba7b0f2b1549a026d9fc414ca2196de603df36fb32633bbdc19 + languageName: node + linkType: hard + "@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.12.1, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" @@ -1422,6 +2013,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-numeric-separator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f370ea584c55bf4040e1f78c80b4eeb1ce2e6aaa74f87d1a48266493c33931d0b6222d8cee3a082383d6bb648ab8d6b7147a06f974d3296ef3bc39c7851683ec + languageName: node + linkType: hard + "@babel/plugin-proposal-object-rest-spread@npm:7.12.1": version: 7.12.1 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.12.1" @@ -1435,7 +2038,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.12.1": +"@babel/plugin-proposal-object-rest-spread@npm:^7.12.1, @babel/plugin-proposal-object-rest-spread@npm:^7.20.7": version: 7.20.7 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" dependencies: @@ -1450,7 +2053,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.12.7, @babel/plugin-proposal-optional-chaining@npm:^7.18.9": +"@babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7b5b39fb5d8d6d14faad6cb68ece5eeb2fd550fb66b5af7d7582402f974f5bc3684641f7c192a5a57e0f59acfae4aada6786be1eba030881ddc590666eff4d1e + languageName: node + linkType: hard + +"@babel/plugin-proposal-optional-chaining@npm:^7.12.7, @babel/plugin-proposal-optional-chaining@npm:^7.18.9, @babel/plugin-proposal-optional-chaining@npm:^7.20.7, @babel/plugin-proposal-optional-chaining@npm:^7.21.0": version: 7.21.0 resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" dependencies: @@ -1463,7 +2078,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-methods@npm:^7.12.1": +"@babel/plugin-proposal-private-methods@npm:^7.12.1, @babel/plugin-proposal-private-methods@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" dependencies: @@ -1484,7 +2099,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.12.1": +"@babel/plugin-proposal-private-property-in-object@npm:^7.12.1, @babel/plugin-proposal-private-property-in-object@npm:^7.21.0": version: 7.21.0 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0" dependencies: @@ -1498,7 +2113,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": +"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": version: 7.18.6 resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" dependencies: @@ -1609,6 +2224,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-assertions@npm:^7.20.0": + version: 7.20.0 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" + dependencies: + "@babel/helper-plugin-utils": ^7.19.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6a86220e0aae40164cd3ffaf80e7c076a1be02a8f3480455dddbae05fda8140f429290027604df7a11b3f3f124866e8a6d69dbfa1dda61ee7377b920ad144d5b + languageName: node + linkType: hard + "@babel/plugin-syntax-import-assertions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-import-assertions@npm:7.22.5" @@ -1664,7 +2290,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.22.5, @babel/plugin-syntax-jsx@npm:^7.7.2": +"@babel/plugin-syntax-jsx@npm:^7.18.6, @babel/plugin-syntax-jsx@npm:^7.21.4, @babel/plugin-syntax-jsx@npm:^7.7.2": + version: 7.21.4 + resolution: "@babel/plugin-syntax-jsx@npm:7.21.4" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bb7309402a1d4e155f32aa0cf216e1fa8324d6c4cfd248b03280028a015a10e46b6efd6565f515f8913918a3602b39255999c06046f7d4b8a5106be2165d724a + languageName: node + linkType: hard + +"@babel/plugin-syntax-jsx@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" dependencies: @@ -1763,7 +2400,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.22.5, @babel/plugin-syntax-typescript@npm:^7.7.2": +"@babel/plugin-syntax-typescript@npm:^7.20.0, @babel/plugin-syntax-typescript@npm:^7.7.2": + version: 7.21.4 + resolution: "@babel/plugin-syntax-typescript@npm:7.21.4" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a59ce2477b7ae8c8945dc37dda292fef9ce46a6507b3d76b03ce7f3a6c9451a6567438b20a78ebcb3955d04095fd1ccd767075a863f79fcc30aa34dcfa441fe0 + languageName: node + linkType: hard + +"@babel/plugin-syntax-typescript@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" dependencies: @@ -1786,7 +2434,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.22.5": +"@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b43cabe3790c2de7710abe32df9a30005eddb2050dadd5d122c6872f679e5710e410f1b90c8f99a2aff7b614cccfecf30e7fd310236686f60d3ed43fd80b9847 + languageName: node + linkType: hard + +"@babel/plugin-transform-arrow-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-arrow-functions@npm:7.22.5" dependencies: @@ -1811,6 +2470,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-to-generator@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" + dependencies: + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 + languageName: node + linkType: hard + "@babel/plugin-transform-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-async-to-generator@npm:7.22.5" @@ -1824,6 +2496,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0a0df61f94601e3666bf39f2cc26f5f7b22a94450fb93081edbed967bd752ce3f81d1227fefd3799f5ee2722171b5e28db61379234d1bb85b6ec689589f99d7e + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoped-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" @@ -1835,7 +2518,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.22.5": +"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 15aacaadbecf96b53a750db1be4990b0d89c7f5bc3e1794b63b49fb219638c1fd25d452d15566d7e5ddf5b5f4e1a0a0055c35c1c7aee323c7b114bf49f66f4b0 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoping@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoping@npm:7.22.5" dependencies: @@ -1871,7 +2565,26 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.22.5": +"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/plugin-transform-classes@npm:7.21.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-replace-supers": ^7.20.7 + "@babel/helper-split-export-declaration": ^7.18.6 + globals: ^11.1.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 088ae152074bd0e90f64659169255bfe50393e637ec8765cb2a518848b11b0299e66b91003728fd0a41563a6fdc6b8d548ece698a314fd5447f5489c22e466b7 + languageName: node + linkType: hard + +"@babel/plugin-transform-classes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-classes@npm:7.22.5" dependencies: @@ -1890,6 +2603,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-computed-properties@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/template": ^7.20.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: be70e54bda8b469146459f429e5f2bd415023b87b2d5af8b10e48f465ffb02847a3ed162ca60378c004b82db848e4d62e90010d41ded7e7176b6d8d1c2911139 + languageName: node + linkType: hard + "@babel/plugin-transform-computed-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-computed-properties@npm:7.22.5" @@ -1902,7 +2627,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.22.5": +"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 43ebbe0bfa20287e34427be7c2200ce096c20913775ea75268fb47fe0e55f9510800587e6052c42fe6dffa0daaad95dd465c3e312fd1ef9785648384c45417ac + languageName: node + linkType: hard + +"@babel/plugin-transform-destructuring@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-destructuring@npm:7.22.5" dependencies: @@ -1913,7 +2649,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.22.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": +"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.4.4": + version: 7.18.6 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cbe5d7063eb8f8cca24cd4827bc97f5641166509e58781a5f8aa47fb3d2d786ce4506a30fca2e01f61f18792783a5cb5d96bf5434c3dd1ad0de8c9cc625a53da + languageName: node + linkType: hard + +"@babel/plugin-transform-dotall-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" dependencies: @@ -1925,6 +2673,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 220bf4a9fec5c4d4a7b1de38810350260e8ea08481bf78332a464a21256a95f0df8cd56025f346238f09b04f8e86d4158fafc9f4af57abaef31637e3b58bd4fe + languageName: node + linkType: hard + "@babel/plugin-transform-duplicate-keys@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" @@ -1948,6 +2707,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7f70222f6829c82a36005508d34ddbe6fd0974ae190683a8670dd6ff08669aaf51fef2209d7403f9bd543cb2d12b18458016c99a6ed0332ccedb3ea127b01229 + languageName: node + linkType: hard + "@babel/plugin-transform-exponentiation-operator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.22.5" @@ -1984,7 +2755,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.22.5": +"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/plugin-transform-for-of@npm:7.21.0" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2f3f86ca1fab2929fcda6a87e4303d5c635b5f96dc9a45fd4ca083308a3020c79ac33b9543eb4640ef2b79f3586a00ab2d002a7081adb9e9d7440dce30781034 + languageName: node + linkType: hard + +"@babel/plugin-transform-for-of@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-for-of@npm:7.22.5" dependencies: @@ -1995,6 +2777,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-function-name@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-function-name@npm:7.18.9" + dependencies: + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-function-name": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 62dd9c6cdc9714704efe15545e782ee52d74dc73916bf954b4d3bee088fb0ec9e3c8f52e751252433656c09f744b27b757fc06ed99bcde28e8a21600a1d8e597 + languageName: node + linkType: hard + "@babel/plugin-transform-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-function-name@npm:7.22.5" @@ -2020,6 +2815,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-literals@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-literals@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3458dd2f1a47ac51d9d607aa18f3d321cbfa8560a985199185bed5a906bb0c61ba85575d386460bac9aed43fdd98940041fae5a67dff286f6f967707cff489f8 + languageName: node + linkType: hard + "@babel/plugin-transform-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-literals@npm:7.22.5" @@ -2043,6 +2849,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 35a3d04f6693bc6b298c05453d85ee6e41cc806538acb6928427e0e97ae06059f97d2f07d21495fcf5f70d3c13a242e2ecbd09d5c1fcb1b1a73ff528dcb0b695 + languageName: node + linkType: hard + "@babel/plugin-transform-member-expression-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.22.5" @@ -2054,6 +2871,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-amd@npm:^7.20.11": + version: 7.20.11 + resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" + dependencies: + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 23665c1c20c8f11c89382b588fb9651c0756d130737a7625baeaadbd3b973bc5bfba1303bedffa8fb99db1e6d848afb01016e1df2b69b18303e946890c790001 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-amd@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-amd@npm:7.22.5" @@ -2066,6 +2895,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-commonjs@npm:^7.21.2": + version: 7.21.2 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.2" + dependencies: + "@babel/helper-module-transforms": ^7.21.2 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-simple-access": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 65aa06e3e3792f39b99eb5f807034693ff0ecf80438580f7ae504f4c4448ef04147b1889ea5e6f60f3ad4a12ebbb57c6f1f979a249dadbd8d11fe22f4441918b + languageName: node + linkType: hard + "@babel/plugin-transform-modules-commonjs@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5" @@ -2079,6 +2921,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-systemjs@npm:^7.20.11": + version: 7.20.11 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11" + dependencies: + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-validator-identifier": ^7.19.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4546c47587f88156d66c7eb7808e903cf4bb3f6ba6ac9bc8e3af2e29e92eb9f0b3f44d52043bfd24eb25fa7827fd7b6c8bfeac0cac7584e019b87e1ecbd0e673 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-systemjs@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.5" @@ -2093,6 +2949,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-umd@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" + dependencies: + "@babel/helper-module-transforms": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c3b6796c6f4579f1ba5ab0cdcc73910c1e9c8e1e773c507c8bb4da33072b3ae5df73c6d68f9126dab6e99c24ea8571e1563f8710d7c421fac1cde1e434c20153 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-umd@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-umd@npm:7.22.5" @@ -2105,6 +2973,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.20.5": + version: 7.20.5 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.20.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.20.5 + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 528c95fb1087e212f17e1c6456df041b28a83c772b9c93d2e407c9d03b72182b0d9d126770c1d6e0b23aab052599ceaf25ed6a2c0627f4249be34a83f6fae853 + languageName: node + linkType: hard + "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" @@ -2117,6 +2997,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-new-target@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-new-target@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bd780e14f46af55d0ae8503b3cb81ca86dcc73ed782f177e74f498fff934754f9e9911df1f8f3bd123777eed7c1c1af4d66abab87c8daae5403e7719a6b845d1 + languageName: node + linkType: hard + "@babel/plugin-transform-new-target@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-new-target@npm:7.22.5" @@ -2167,6 +3058,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-super@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-object-super@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-replace-supers": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0fcb04e15deea96ae047c21cb403607d49f06b23b4589055993365ebd7a7d7541334f06bf9642e90075e66efce6ebaf1eb0ef066fbbab802d21d714f1aac3aef + languageName: node + linkType: hard + "@babel/plugin-transform-object-super@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-object-super@npm:7.22.5" @@ -2204,7 +3107,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.22.5": +"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/plugin-transform-parameters@npm:7.21.3" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c92128d7b1fcf54e2cab186c196bbbf55a9a6de11a83328dc2602649c9dc6d16ef73712beecd776cd49bfdc624b5f56740f4a53568d3deb9505ec666bc869da3 + languageName: node + linkType: hard + +"@babel/plugin-transform-parameters@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-parameters@npm:7.22.5" dependencies: @@ -2241,6 +3155,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-property-literals@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1c16e64de554703f4b547541de2edda6c01346dd3031d4d29e881aa7733785cd26d53611a4ccf5353f4d3e69097bb0111c0a93ace9e683edd94fea28c4484144 + languageName: node + linkType: hard + "@babel/plugin-transform-property-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-property-literals@npm:7.22.5" @@ -2252,6 +3177,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-display-name@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-display-name@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 51c087ab9e41ef71a29335587da28417536c6f816c292e092ffc0e0985d2f032656801d4dd502213ce32481f4ba6c69402993ffa67f0818a07606ff811e4be49 + languageName: node + linkType: hard + "@babel/plugin-transform-react-display-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-display-name@npm:7.22.5" @@ -2263,6 +3199,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx-development@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.18.6" + dependencies: + "@babel/plugin-transform-react-jsx": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ec9fa65db66f938b75c45e99584367779ac3e0af8afc589187262e1337c7c4205ea312877813ae4df9fb93d766627b8968d74ac2ba702e4883b1dbbe4953ecee + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx-development@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx-development@npm:7.22.5" @@ -2296,7 +3243,22 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.12.12, @babel/plugin-transform-react-jsx@npm:^7.22.5": +"@babel/plugin-transform-react-jsx@npm:^7.12.12, @babel/plugin-transform-react-jsx@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-jsx@npm:7.18.6" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-jsx": ^7.18.6 + "@babel/types": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 46129eaf1ab7a7a73e3e8c9d9859b630f5b381c5e19fb1559e2db7b943a7825b6715ad950623fb03fe7bd31ed618ce1d0bd539b13fa030a50c39d5a873a5ba00 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx@npm:7.22.5" dependencies: @@ -2311,6 +3273,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-pure-annotations@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.18.6" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 97c4873d409088f437f9084d084615948198dd87fc6723ada0e7e29c5a03623c2f3e03df3f52e7e7d4d23be32a08ea00818bff302812e48713c706713bd06219 + languageName: node + linkType: hard + "@babel/plugin-transform-react-pure-annotations@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.22.5" @@ -2323,6 +3297,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-regenerator@npm:^7.20.5": + version: 7.20.5 + resolution: "@babel/plugin-transform-regenerator@npm:7.20.5" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + regenerator-transform: ^0.15.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 13164861e71fb23d84c6270ef5330b03c54d5d661c2c7468f28e21c4f8598558ca0c8c3cb1d996219352946e849d270a61372bc93c8fbe9676e78e3ffd0dea07 + languageName: node + linkType: hard + "@babel/plugin-transform-regenerator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-regenerator@npm:7.22.5" @@ -2335,6 +3321,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-reserved-words@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0738cdc30abdae07c8ec4b233b30c31f68b3ff0eaa40eddb45ae607c066127f5fa99ddad3c0177d8e2832e3a7d3ad115775c62b431ebd6189c40a951b867a80c + languageName: node + linkType: hard + "@babel/plugin-transform-reserved-words@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" @@ -2346,7 +3343,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.12.1, @babel/plugin-transform-shorthand-properties@npm:^7.22.5": +"@babel/plugin-transform-shorthand-properties@npm:^7.12.1, @babel/plugin-transform-shorthand-properties@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b8e4e8acc2700d1e0d7d5dbfd4fdfb935651913de6be36e6afb7e739d8f9ca539a5150075a0f9b79c88be25ddf45abb912fe7abf525f0b80f5b9d9860de685d7 + languageName: node + linkType: hard + +"@babel/plugin-transform-shorthand-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" dependencies: @@ -2357,7 +3365,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.22.5": +"@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/plugin-transform-spread@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8ea698a12da15718aac7489d4cde10beb8a3eea1f66167d11ab1e625033641e8b328157fd1a0b55dd6531933a160c01fc2e2e61132a385cece05f26429fd0cc2 + languageName: node + linkType: hard + +"@babel/plugin-transform-spread@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-spread@npm:7.22.5" dependencies: @@ -2369,6 +3389,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-sticky-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 68ea18884ae9723443ffa975eb736c8c0d751265859cd3955691253f7fee37d7a0f7efea96c8a062876af49a257a18ea0ed5fea0d95a7b3611ce40f7ee23aee3 + languageName: node + linkType: hard + "@babel/plugin-transform-sticky-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" @@ -2380,7 +3411,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.22.5": +"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3d2fcd79b7c345917f69b92a85bdc3ddd68ce2c87dc70c7d61a8373546ccd1f5cb8adc8540b49dfba08e1b82bb7b3bbe23a19efdb2b9c994db2db42906ca9fb2 + languageName: node + linkType: hard + +"@babel/plugin-transform-template-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" dependencies: @@ -2391,6 +3433,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e754e0d8b8a028c52e10c148088606e3f7a9942c57bd648fc0438e5b4868db73c386a5ed47ab6d6f0594aae29ee5ffc2ffc0f7ebee7fae560a066d6dea811cd4 + languageName: node + linkType: hard + "@babel/plugin-transform-typeof-symbol@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" @@ -2402,6 +3455,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typescript@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/plugin-transform-typescript@npm:7.21.3" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-typescript": ^7.20.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c16fd577bf43f633deb76fca2a8527d8ae25968c8efdf327c1955472c3e0257e62992473d1ad7f9ee95379ce2404699af405ea03346055adadd3478ad0ecd117 + languageName: node + linkType: hard + "@babel/plugin-transform-typescript@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typescript@npm:7.22.5" @@ -2416,6 +3483,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-escapes@npm:^7.18.10": + version: 7.21.5 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.21.5" + dependencies: + "@babel/helper-plugin-utils": ^7.21.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6504d642d0449a275191b624bd94d3e434ae154e610bf2f0e3c109068b287d2474f68e1da64b47f21d193cd67b27ee4643877d530187670565cac46e29fd257d + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-escapes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.5" @@ -2439,6 +3517,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d9e18d57536a2d317fb0b7c04f8f55347f3cfacb75e636b4c6fa2080ab13a3542771b5120e726b598b815891fc606d1472ac02b749c69fd527b03847f22dc25e + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" @@ -2463,7 +3553,92 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.20.2, @babel/preset-env@npm:^7.21.4, @babel/preset-env@npm:~7.22.5": +"@babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/preset-env@npm:7.21.4" + dependencies: + "@babel/compat-data": ^7.21.4 + "@babel/helper-compilation-targets": ^7.21.4 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-validator-option": ^7.21.0 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.20.7 + "@babel/plugin-proposal-async-generator-functions": ^7.20.7 + "@babel/plugin-proposal-class-properties": ^7.18.6 + "@babel/plugin-proposal-class-static-block": ^7.21.0 + "@babel/plugin-proposal-dynamic-import": ^7.18.6 + "@babel/plugin-proposal-export-namespace-from": ^7.18.9 + "@babel/plugin-proposal-json-strings": ^7.18.6 + "@babel/plugin-proposal-logical-assignment-operators": ^7.20.7 + "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 + "@babel/plugin-proposal-numeric-separator": ^7.18.6 + "@babel/plugin-proposal-object-rest-spread": ^7.20.7 + "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 + "@babel/plugin-proposal-optional-chaining": ^7.21.0 + "@babel/plugin-proposal-private-methods": ^7.18.6 + "@babel/plugin-proposal-private-property-in-object": ^7.21.0 + "@babel/plugin-proposal-unicode-property-regex": ^7.18.6 + "@babel/plugin-syntax-async-generators": ^7.8.4 + "@babel/plugin-syntax-class-properties": ^7.12.13 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + "@babel/plugin-syntax-import-assertions": ^7.20.0 + "@babel/plugin-syntax-json-strings": ^7.8.3 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + "@babel/plugin-syntax-top-level-await": ^7.14.5 + "@babel/plugin-transform-arrow-functions": ^7.20.7 + "@babel/plugin-transform-async-to-generator": ^7.20.7 + "@babel/plugin-transform-block-scoped-functions": ^7.18.6 + "@babel/plugin-transform-block-scoping": ^7.21.0 + "@babel/plugin-transform-classes": ^7.21.0 + "@babel/plugin-transform-computed-properties": ^7.20.7 + "@babel/plugin-transform-destructuring": ^7.21.3 + "@babel/plugin-transform-dotall-regex": ^7.18.6 + "@babel/plugin-transform-duplicate-keys": ^7.18.9 + "@babel/plugin-transform-exponentiation-operator": ^7.18.6 + "@babel/plugin-transform-for-of": ^7.21.0 + "@babel/plugin-transform-function-name": ^7.18.9 + "@babel/plugin-transform-literals": ^7.18.9 + "@babel/plugin-transform-member-expression-literals": ^7.18.6 + "@babel/plugin-transform-modules-amd": ^7.20.11 + "@babel/plugin-transform-modules-commonjs": ^7.21.2 + "@babel/plugin-transform-modules-systemjs": ^7.20.11 + "@babel/plugin-transform-modules-umd": ^7.18.6 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.20.5 + "@babel/plugin-transform-new-target": ^7.18.6 + "@babel/plugin-transform-object-super": ^7.18.6 + "@babel/plugin-transform-parameters": ^7.21.3 + "@babel/plugin-transform-property-literals": ^7.18.6 + "@babel/plugin-transform-regenerator": ^7.20.5 + "@babel/plugin-transform-reserved-words": ^7.18.6 + "@babel/plugin-transform-shorthand-properties": ^7.18.6 + "@babel/plugin-transform-spread": ^7.20.7 + "@babel/plugin-transform-sticky-regex": ^7.18.6 + "@babel/plugin-transform-template-literals": ^7.18.9 + "@babel/plugin-transform-typeof-symbol": ^7.18.9 + "@babel/plugin-transform-unicode-escapes": ^7.18.10 + "@babel/plugin-transform-unicode-regex": ^7.18.6 + "@babel/preset-modules": ^0.1.5 + "@babel/types": ^7.21.4 + babel-plugin-polyfill-corejs2: ^0.3.3 + babel-plugin-polyfill-corejs3: ^0.6.0 + babel-plugin-polyfill-regenerator: ^0.4.1 + core-js-compat: ^3.25.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1e328674c4b39e985fa81e5a8eee9aaab353dea4ff1f28f454c5e27a6498c762e25d42e827f5bfc9d7acf6c9b8bc317b5283aa7c83d9fd03c1a89e5c08f334f9 + languageName: node + linkType: hard + +"@babel/preset-env@npm:^7.20.2, @babel/preset-env@npm:~7.22.5": version: 7.22.5 resolution: "@babel/preset-env@npm:7.22.5" dependencies: @@ -2581,7 +3756,23 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.12.10, @babel/preset-react@npm:^7.18.6": +"@babel/preset-react@npm:^7.12.10": + version: 7.18.6 + resolution: "@babel/preset-react@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-validator-option": ^7.18.6 + "@babel/plugin-transform-react-display-name": ^7.18.6 + "@babel/plugin-transform-react-jsx": ^7.18.6 + "@babel/plugin-transform-react-jsx-development": ^7.18.6 + "@babel/plugin-transform-react-pure-annotations": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 540d9cf0a0cc0bb07e6879994e6fb7152f87dafbac880b56b65e2f528134c7ba33e0cd140b58700c77b2ebf4c81fa6468fed0ba391462d75efc7f8c1699bb4c3 + languageName: node + linkType: hard + +"@babel/preset-react@npm:^7.18.6": version: 7.22.5 resolution: "@babel/preset-react@npm:7.22.5" dependencies: @@ -2597,7 +3788,22 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.12.7, @babel/preset-typescript@npm:^7.21.4, @babel/preset-typescript@npm:~7.22.5": +"@babel/preset-typescript@npm:^7.12.7, @babel/preset-typescript@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/preset-typescript@npm:7.21.4" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-validator-option": ^7.21.0 + "@babel/plugin-syntax-jsx": ^7.21.4 + "@babel/plugin-transform-modules-commonjs": ^7.21.2 + "@babel/plugin-transform-typescript": ^7.21.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 83b2f2bf7be3a970acd212177525f58bbb1f2e042b675a47d021a675ae27cf00b6b6babfaf3ae5c980592c9ed1b0712e5197796b691905d25c99f9006478ea06 + languageName: node + linkType: hard + +"@babel/preset-typescript@npm:~7.22.5": version: 7.22.5 resolution: "@babel/preset-typescript@npm:7.22.5" dependencies: @@ -2612,7 +3818,22 @@ __metadata: languageName: node linkType: hard -"@babel/register@npm:^7.12.1, @babel/register@npm:^7.18.9": +"@babel/register@npm:^7.12.1": + version: 7.18.9 + resolution: "@babel/register@npm:7.18.9" + dependencies: + clone-deep: ^4.0.1 + find-cache-dir: ^2.0.0 + make-dir: ^2.1.0 + pirates: ^4.0.5 + source-map-support: ^0.5.16 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4aeaff97e061a397f632659082ba86c539ef8194697b236d991c10d1c2ea8f73213d3b5b3b2c24625951a1ef726b7a7d2e70f70ffcb37f79ef0c1a745eebef21 + languageName: node + linkType: hard + +"@babel/register@npm:^7.18.9": version: 7.22.5 resolution: "@babel/register@npm:7.22.5" dependencies: @@ -2643,7 +3864,25 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2, @babel/runtime@npm:~7.22.5": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": + version: 7.21.0 + resolution: "@babel/runtime@npm:7.21.0" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.20.1": + version: 7.21.5 + resolution: "@babel/runtime@npm:7.21.5" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 358f2779d3187f5c67ad302e8f8d435412925d0b991d133c7d4a7b1ddd5a3fda1b6f34537cb64628dfd96a27ae46df105bed3895b8d754b88cacdded8d1129dd + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.20.13, @babel/runtime@npm:~7.22.5": version: 7.22.5 resolution: "@babel/runtime@npm:7.22.5" dependencies: @@ -2652,6 +3891,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.20.6": + version: 7.20.7 + resolution: "@babel/runtime@npm:7.20.7" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 4629ce5c46f06cca9cfb9b7fc00d48003335a809888e2b91ec2069a2dcfbfef738480cff32ba81e0b7c290f8918e5c22ddcf2b710001464ee84ba62c7e32a3a3 + languageName: node + linkType: hard + "@babel/runtime@npm:~7.5.4": version: 7.5.5 resolution: "@babel/runtime@npm:7.5.5" @@ -2661,7 +3909,18 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.12.7, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.12.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.3.3": + version: 7.20.7 + resolution: "@babel/template@npm:7.20.7" + dependencies: + "@babel/code-frame": ^7.18.6 + "@babel/parser": ^7.20.7 + "@babel/types": ^7.20.7 + checksum: 2eb1a0ab8d415078776bceb3473d07ab746e6bb4c2f6ca46ee70efb284d75c4a32bb0cd6f4f4946dec9711f9c0780e8e5d64b743208deac6f8e9858afadc349e + languageName: node + linkType: hard + +"@babel/template@npm:^7.22.5": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" dependencies: @@ -2672,7 +3931,43 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.22.5, @babel/traverse@npm:^7.7.2": +"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.19.0, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4, @babel/traverse@npm:^7.7.2": + version: 7.21.4 + resolution: "@babel/traverse@npm:7.21.4" + dependencies: + "@babel/code-frame": ^7.21.4 + "@babel/generator": ^7.21.4 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/parser": ^7.21.4 + "@babel/types": ^7.21.4 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: f22f067c2d9b6497abf3d4e53ea71f3aa82a21f2ed434dd69b8c5767f11f2a4c24c8d2f517d2312c9e5248e5c69395fdca1c95a2b3286122c75f5783ddb6f53c + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.21.5": + version: 7.21.5 + resolution: "@babel/traverse@npm:7.21.5" + dependencies: + "@babel/code-frame": ^7.21.4 + "@babel/generator": ^7.21.5 + "@babel/helper-environment-visitor": ^7.21.5 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/parser": ^7.21.5 + "@babel/types": ^7.21.5 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: b403733fa7d858f0c8e224f0434a6ade641bc469a4f92975363391e796629d5bf53e544761dfe85039aab92d5389ebe7721edb309d7a5bb7df2bf74f37bf9f47 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.22.5": version: 7.22.5 resolution: "@babel/traverse@npm:7.22.5" dependencies: @@ -2690,7 +3985,29 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.19.0, @babel/types@npm:^7.2.0, @babel/types@npm:^7.21.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.21.5 + resolution: "@babel/types@npm:7.21.5" + dependencies: + "@babel/helper-string-parser": ^7.21.5 + "@babel/helper-validator-identifier": ^7.19.1 + to-fast-properties: ^2.0.0 + checksum: 43242a99c612d13285ee4af46cc0f1066bcb6ffd38307daef7a76e8c70f36cfc3255eb9e75c8e768b40e761176c313aec4d5c0b9d97a21e494d49d5fd123a9f7 + languageName: node + linkType: hard + +"@babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.21.4": + version: 7.21.4 + resolution: "@babel/types@npm:7.21.4" + dependencies: + "@babel/helper-string-parser": ^7.19.4 + "@babel/helper-validator-identifier": ^7.19.1 + to-fast-properties: ^2.0.0 + checksum: 587bc55a91ce003b0f8aa10d70070f8006560d7dc0360dc0406d306a2cb2a10154e2f9080b9c37abec76907a90b330a536406cb75e6bdc905484f37b75c73219 + languageName: node + linkType: hard + +"@babel/types@npm:^7.22.5": version: 7.22.5 resolution: "@babel/types@npm:7.22.5" dependencies: @@ -3325,6 +4642,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/android-arm64@npm:0.17.18" + checksum: ec47777acf96ffe5e36426e5c5715f74e154ddd2a4b2fcd12748250d7b3ded51c5a1a8a5f896f1524e52d3abf4b302aad0b2f30ac23b4efc41de2d01e359a34a + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm64@npm:0.17.19" @@ -3332,6 +4656,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/android-arm@npm:0.17.18" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm@npm:0.17.19" @@ -3339,6 +4670,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/android-x64@npm:0.17.18" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-x64@npm:0.17.19" @@ -3346,6 +4684,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/darwin-arm64@npm:0.17.18" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-arm64@npm:0.17.19" @@ -3353,6 +4698,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/darwin-x64@npm:0.17.18" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-x64@npm:0.17.19" @@ -3360,6 +4712,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/freebsd-arm64@npm:0.17.18" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-arm64@npm:0.17.19" @@ -3367,6 +4726,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/freebsd-x64@npm:0.17.18" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-x64@npm:0.17.19" @@ -3374,6 +4740,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-arm64@npm:0.17.18" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm64@npm:0.17.19" @@ -3381,6 +4754,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-arm@npm:0.17.18" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm@npm:0.17.19" @@ -3388,6 +4768,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-ia32@npm:0.17.18" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ia32@npm:0.17.19" @@ -3395,6 +4782,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-loong64@npm:0.17.18" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-loong64@npm:0.17.19" @@ -3402,6 +4796,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-mips64el@npm:0.17.18" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-mips64el@npm:0.17.19" @@ -3409,6 +4810,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-ppc64@npm:0.17.18" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ppc64@npm:0.17.19" @@ -3416,6 +4824,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-riscv64@npm:0.17.18" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-riscv64@npm:0.17.19" @@ -3423,6 +4838,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-s390x@npm:0.17.18" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-s390x@npm:0.17.19" @@ -3430,6 +4852,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/linux-x64@npm:0.17.18" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-x64@npm:0.17.19" @@ -3437,6 +4866,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/netbsd-x64@npm:0.17.18" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/netbsd-x64@npm:0.17.19" @@ -3444,6 +4880,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/openbsd-x64@npm:0.17.18" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/openbsd-x64@npm:0.17.19" @@ -3451,6 +4894,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/sunos-x64@npm:0.17.18" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/sunos-x64@npm:0.17.19" @@ -3458,6 +4908,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/win32-arm64@npm:0.17.18" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-arm64@npm:0.17.19" @@ -3465,6 +4922,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/win32-ia32@npm:0.17.18" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-ia32@npm:0.17.19" @@ -3472,6 +4936,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.17.18": + version: 0.17.18 + resolution: "@esbuild/win32-x64@npm:0.17.18" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-x64@npm:0.17.19" @@ -3695,6 +5166,15 @@ __metadata: languageName: node linkType: hard +"@internationalized/date@npm:^3.0.1": + version: 3.0.1 + resolution: "@internationalized/date@npm:3.0.1" + dependencies: + "@babel/runtime": ^7.6.2 + checksum: ff51a00550322a5df3d3051e8ffdf3d7741851149e8ba300883e01402249602e87cc50b27b972753d9af88c5374df83c24adf58cae5e269100cb946a3b12cd56 + languageName: node + linkType: hard + "@internationalized/date@npm:^3.2.0": version: 3.2.0 resolution: "@internationalized/date@npm:3.2.0" @@ -3714,6 +5194,16 @@ __metadata: languageName: node linkType: hard +"@internationalized/message@npm:^3.0.9": + version: 3.0.9 + resolution: "@internationalized/message@npm:3.0.9" + dependencies: + "@babel/runtime": ^7.6.2 + intl-messageformat: ^10.1.0 + checksum: b3f7f5a8e1d8df99efb3463ca07edb976ecf95d28de19a47d92fb19c093052b1a092aeaa226dc69d07143854bdbeb8519a0ac8ba8c900c4b0f565151d735ca7f + languageName: node + linkType: hard + "@internationalized/message@npm:^3.1.0": version: 3.1.0 resolution: "@internationalized/message@npm:3.1.0" @@ -3733,6 +5223,15 @@ __metadata: languageName: node linkType: hard +"@internationalized/number@npm:^3.1.1": + version: 3.1.1 + resolution: "@internationalized/number@npm:3.1.1" + dependencies: + "@babel/runtime": ^7.6.2 + checksum: 9979ea1ca7388de75193c9d36f19d928fbcb715d456d153c30cafadd2ce1ceae011f55c966d424f4561ec04de14d3b48b8fe16a9e2737273829a813c4f7203a3 + languageName: node + linkType: hard + "@internationalized/number@npm:^3.2.0": version: 3.2.0 resolution: "@internationalized/number@npm:3.2.0" @@ -3751,6 +5250,15 @@ __metadata: languageName: node linkType: hard +"@internationalized/string@npm:^3.0.0": + version: 3.0.0 + resolution: "@internationalized/string@npm:3.0.0" + dependencies: + "@babel/runtime": ^7.6.2 + checksum: fc347cf80cd4ee009d1c467dca2c6908a919ad152086bf5e8c1a0aede0383fb317695fc5d82abe033ec90ad62108297130b653b63b9529f2e032999798ae4a81 + languageName: node + linkType: hard + "@internationalized/string@npm:^3.1.0": version: 3.1.0 resolution: "@internationalized/string@npm:3.1.0" @@ -4318,13 +5826,20 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": +"@jridgewell/resolve-uri@npm:3.1.0": version: 3.1.0 resolution: "@jridgewell/resolve-uri@npm:3.1.0" checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 languageName: node linkType: hard +"@jridgewell/resolve-uri@npm:^3.0.3": + version: 3.0.5 + resolution: "@jridgewell/resolve-uri@npm:3.0.5" + checksum: 1ee652b693da7979ac4007926cc3f0a32b657ffeb913e111f44e5b67153d94a2f28a1d560101cc0cf8087625468293a69a00f634a2914e1a6d0817ba2039a913 + languageName: node + linkType: hard + "@jridgewell/set-array@npm:^1.0.1": version: 1.1.2 resolution: "@jridgewell/set-array@npm:1.1.2" @@ -4332,6 +5847,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/source-map@npm:^0.3.2": + version: 0.3.2 + resolution: "@jridgewell/source-map@npm:0.3.2" + dependencies: + "@jridgewell/gen-mapping": ^0.3.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1b83f0eb944e77b70559a394d5d3b3f98a81fcc186946aceb3ef42d036762b52ef71493c6c0a3b7c1d2f08785f53ba2df1277fe629a06e6109588ff4cdcf7482 + languageName: node + linkType: hard + "@jridgewell/source-map@npm:^0.3.3": version: 0.3.3 resolution: "@jridgewell/source-map@npm:0.3.3" @@ -4342,13 +5867,20 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10": +"@jridgewell/sourcemap-codec@npm:1.4.14": version: 1.4.14 resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.4.10": + version: 1.4.11 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.11" + checksum: 3b2afaf8400fb07a36db60e901fcce6a746cdec587310ee9035939d89878e57b2dec8173b0b8f63176f647efa352294049a53c49739098eb907ff81fec2547c8 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:0.3.9": version: 0.3.9 resolution: "@jridgewell/trace-mapping@npm:0.3.9" @@ -4359,7 +5891,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.14, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.18 resolution: "@jridgewell/trace-mapping@npm:0.3.18" dependencies: @@ -4407,7 +5939,16 @@ __metadata: languageName: node linkType: hard -"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3, @lezer/highlight@npm:^1.1.6": +"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3": + version: 1.1.5 + resolution: "@lezer/highlight@npm:1.1.5" + dependencies: + "@lezer/common": ^1.0.0 + checksum: 1f0b0a3dc7e1f23d889ce7a61d9ce1ba4d3b307205baf58f97252588df4c6751e4c86d39c20cd0bc7ac39ab82ff78a9251db91148b3b069965ec55d5fe9c4ef5 + languageName: node + linkType: hard + +"@lezer/highlight@npm:^1.1.6": version: 1.1.6 resolution: "@lezer/highlight@npm:1.1.6" dependencies: @@ -5563,6 +7104,24 @@ __metadata: languageName: node linkType: hard +"@react-aria/i18n@npm:^3.6.0": + version: 3.6.0 + resolution: "@react-aria/i18n@npm:3.6.0" + dependencies: + "@babel/runtime": ^7.6.2 + "@internationalized/date": ^3.0.1 + "@internationalized/message": ^3.0.9 + "@internationalized/number": ^3.1.1 + "@internationalized/string": ^3.0.0 + "@react-aria/ssr": ^3.3.0 + "@react-aria/utils": ^3.13.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: ede9cd611e15fe2975556dfe695bdcb67cbcb8d2dfff7677174f86f1418421491fbbbfd8eab40e724a8db24877d2f980df6e50d26d29d5b3e607ca39b42befc3 + languageName: node + linkType: hard + "@react-aria/i18n@npm:^3.7.0, @react-aria/i18n@npm:^3.7.1": version: 3.7.1 resolution: "@react-aria/i18n@npm:3.7.1" @@ -5968,6 +7527,17 @@ __metadata: languageName: node linkType: hard +"@react-aria/ssr@npm:^3.3.0": + version: 3.3.0 + resolution: "@react-aria/ssr@npm:3.3.0" + dependencies: + "@babel/runtime": ^7.6.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 0b7677ef521c65452460601dce3c264b67baa75ef7c99e9755ea55913765054156b6157c9c42e3d56aba86d1704b8b2aeb7672e4084f2f375fe1ec481e33c8c6 + languageName: node + linkType: hard + "@react-aria/ssr@npm:^3.5.0, @react-aria/ssr@npm:^3.6.0": version: 3.6.0 resolution: "@react-aria/ssr@npm:3.6.0" @@ -6122,6 +7692,21 @@ __metadata: languageName: node linkType: hard +"@react-aria/utils@npm:^3.13.3": + version: 3.13.3 + resolution: "@react-aria/utils@npm:3.13.3" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-aria/ssr": ^3.3.0 + "@react-stately/utils": ^3.5.1 + "@react-types/shared": ^3.14.1 + clsx: ^1.1.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: b6d87ddb8e1d93b00405473099390c854647d81c0419de53cc4a7f02bdcca6d030776fba9f4b241400af13082bafc820dd5ce05c168e8f5a2c43a1b2660fb2ad + languageName: node + linkType: hard + "@react-aria/utils@npm:^3.15.0, @react-aria/utils@npm:^3.16.0": version: 3.16.0 resolution: "@react-aria/utils@npm:3.16.0" @@ -6400,7 +7985,23 @@ __metadata: languageName: node linkType: hard -"@react-stately/calendar@npm:^3.0.2, @react-stately/calendar@npm:^3.2.0": +"@react-stately/calendar@npm:^3.0.2": + version: 3.0.2 + resolution: "@react-stately/calendar@npm:3.0.2" + dependencies: + "@babel/runtime": ^7.6.2 + "@internationalized/date": ^3.0.1 + "@react-stately/utils": ^3.5.1 + "@react-types/calendar": ^3.0.2 + "@react-types/datepicker": ^3.1.1 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: c093cab8761b1e16603abcde63f78dfefdb7fdf4cc269e41602ab3a7c93f9391d29ac68cc66e030c553305af7d96ff9afa3795123211a59316819937a8181956 + languageName: node + linkType: hard + +"@react-stately/calendar@npm:^3.2.0": version: 3.2.0 resolution: "@react-stately/calendar@npm:3.2.0" dependencies: @@ -6416,7 +8017,21 @@ __metadata: languageName: node linkType: hard -"@react-stately/checkbox@npm:^3.2.1, @react-stately/checkbox@npm:^3.4.1": +"@react-stately/checkbox@npm:^3.2.1": + version: 3.2.1 + resolution: "@react-stately/checkbox@npm:3.2.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/toggle": ^3.4.1 + "@react-stately/utils": ^3.5.1 + "@react-types/checkbox": ^3.3.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 9035b595fa21cc1bef7e04249ec9df2293e93310dd644e4d32087ce19bd77aae38db3e676f6fdffbde875bc9a318f05dd60c61ab6e0d9b524222438e7ef31cd7 + languageName: node + linkType: hard + +"@react-stately/checkbox@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/checkbox@npm:3.4.1" dependencies: @@ -6431,7 +8046,19 @@ __metadata: languageName: node linkType: hard -"@react-stately/collections@npm:^3.4.3, @react-stately/collections@npm:^3.7.0": +"@react-stately/collections@npm:^3.4.3": + version: 3.4.3 + resolution: "@react-stately/collections@npm:3.4.3" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: f9045cdac0b20f7d7464ac37c0402511f7c5a727676d0cfefef74a553247d0dd1c816ea5804aac318d85ea5708599f9c9c2e8bd37165b5c6eec100e27f3832b9 + languageName: node + linkType: hard + +"@react-stately/collections@npm:^3.7.0": version: 3.7.0 resolution: "@react-stately/collections@npm:3.7.0" dependencies: @@ -6461,7 +8088,24 @@ __metadata: languageName: node linkType: hard -"@react-stately/combobox@npm:^3.2.1, @react-stately/combobox@npm:^3.5.0": +"@react-stately/combobox@npm:^3.2.1": + version: 3.2.1 + resolution: "@react-stately/combobox@npm:3.2.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/list": ^3.5.3 + "@react-stately/menu": ^3.4.1 + "@react-stately/select": ^3.3.1 + "@react-stately/utils": ^3.5.1 + "@react-types/combobox": ^3.5.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 3e9a9050e8e20c96ae703876e652d28d2e3cf9dca79008d8e0f9fd096e88f74215add97e7d4aec9fe93afd64ebd676e5593d5178a28ad76c180207740fc47712 + languageName: node + linkType: hard + +"@react-stately/combobox@npm:^3.5.0": version: 3.5.0 resolution: "@react-stately/combobox@npm:3.5.0" dependencies: @@ -6491,7 +8135,24 @@ __metadata: languageName: node linkType: hard -"@react-stately/datepicker@npm:^3.0.2, @react-stately/datepicker@npm:^3.4.0": +"@react-stately/datepicker@npm:^3.0.2": + version: 3.0.2 + resolution: "@react-stately/datepicker@npm:3.0.2" + dependencies: + "@babel/runtime": ^7.6.2 + "@internationalized/date": ^3.0.1 + "@internationalized/string": ^3.0.0 + "@react-stately/overlays": ^3.4.1 + "@react-stately/utils": ^3.5.1 + "@react-types/datepicker": ^3.1.1 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: d0250033d8f4625442177eac1ced6fe446877df9607bd1d7bdea11daae47166072304ee66d4ce1fe12886ef24c0cc1983ac5807a1fe07b05a5749d6b8302f47b + languageName: node + linkType: hard + +"@react-stately/datepicker@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/datepicker@npm:3.4.0" dependencies: @@ -6521,6 +8182,20 @@ __metadata: languageName: node linkType: hard +"@react-stately/grid@npm:^3.3.1": + version: 3.3.1 + resolution: "@react-stately/grid@npm:3.3.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/selection": ^3.10.3 + "@react-types/grid": ^3.1.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 84e1f24d2dcac51b1ab99f0ad403c965eb9988fa236054a5c137efb1917a455d56a1b78f820a77c3af38895d60a24884cfeac5a482b36390b629612ee8c7e7f3 + languageName: node + linkType: hard + "@react-stately/grid@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/grid@npm:3.6.0" @@ -6553,7 +8228,22 @@ __metadata: languageName: node linkType: hard -"@react-stately/list@npm:^3.5.3, @react-stately/list@npm:^3.8.0": +"@react-stately/list@npm:^3.5.3": + version: 3.5.3 + resolution: "@react-stately/list@npm:3.5.3" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/collections": ^3.4.3 + "@react-stately/selection": ^3.10.3 + "@react-stately/utils": ^3.5.1 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 162ba719db06a1649bbeb655c78e8a3f3c17a4c02f3318479ce2cc71940052f4a3cc98e67fd604f48ed89f199c731fb6d7c4d6e7b36d53593a0fc9b38d5e465c + languageName: node + linkType: hard + +"@react-stately/list@npm:^3.8.0": version: 3.8.0 resolution: "@react-stately/list@npm:3.8.0" dependencies: @@ -6568,7 +8258,22 @@ __metadata: languageName: node linkType: hard -"@react-stately/menu@npm:^3.4.1, @react-stately/menu@npm:^3.5.1": +"@react-stately/menu@npm:^3.4.1": + version: 3.4.1 + resolution: "@react-stately/menu@npm:3.4.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/overlays": ^3.4.1 + "@react-stately/utils": ^3.5.1 + "@react-types/menu": ^3.7.1 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: a944d6e3a3caf400ffc52738ee8d586db6c6846d0ecc009de4bbedc88202f63d6bbddbd3d577f730f98f28404b077676af4c307f4ba09314c79cf56087a5aa8c + languageName: node + linkType: hard + +"@react-stately/menu@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/menu@npm:3.5.1" dependencies: @@ -6583,7 +8288,22 @@ __metadata: languageName: node linkType: hard -"@react-stately/numberfield@npm:^3.2.1, @react-stately/numberfield@npm:^3.4.1": +"@react-stately/numberfield@npm:^3.2.1": + version: 3.2.1 + resolution: "@react-stately/numberfield@npm:3.2.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@internationalized/number": ^3.1.1 + "@react-stately/utils": ^3.5.1 + "@react-types/numberfield": ^3.3.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 5698d237c8fbe65cc7ab85c586ffadd92d085f15cab542003419deeccc2f13f2aa839dc844df8853648d892d6580fd4dd15a0b0d4eba86a467afbdb8d3c1675f + languageName: node + linkType: hard + +"@react-stately/numberfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/numberfield@npm:3.4.1" dependencies: @@ -6598,7 +8318,20 @@ __metadata: languageName: node linkType: hard -"@react-stately/overlays@npm:^3.4.1, @react-stately/overlays@npm:^3.5.1": +"@react-stately/overlays@npm:^3.4.1": + version: 3.4.1 + resolution: "@react-stately/overlays@npm:3.4.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/utils": ^3.5.1 + "@react-types/overlays": ^3.6.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 3e0e8711c55198b75cb23a682530969c997fdd21c280a9a1356327ff3806252a70ef13e4efc7734902edfd58d6c2cc9d2624a37d8394ad44e9d33b09186510e3 + languageName: node + linkType: hard + +"@react-stately/overlays@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/overlays@npm:3.5.1" dependencies: @@ -6625,7 +8358,20 @@ __metadata: languageName: node linkType: hard -"@react-stately/radio@npm:^3.5.1, @react-stately/radio@npm:^3.8.0": +"@react-stately/radio@npm:^3.5.1": + version: 3.5.1 + resolution: "@react-stately/radio@npm:3.5.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/utils": ^3.5.1 + "@react-types/radio": ^3.2.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 7a60de8afb5d8ccaf33da66613ae55a4b2eca75bacae902574282c33ab66684b1ae5db95b2743fdcc926d1c0464af7e6d837f6a5b85bb00836a9c78ba65c3623 + languageName: node + linkType: hard + +"@react-stately/radio@npm:^3.8.0": version: 3.8.0 resolution: "@react-stately/radio@npm:3.8.0" dependencies: @@ -6639,7 +8385,21 @@ __metadata: languageName: node linkType: hard -"@react-stately/searchfield@npm:^3.3.1, @react-stately/searchfield@npm:^3.4.1": +"@react-stately/searchfield@npm:^3.3.1": + version: 3.3.1 + resolution: "@react-stately/searchfield@npm:3.3.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/utils": ^3.5.1 + "@react-types/searchfield": ^3.3.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: f52776a294450382ea9f2abacea6b2972ea4f96ef6ffaff33c62f783881ceb74cd6aec959178499d6c7acf49b3a671d1902f0eb9cc4f2dba486ca88f7514693b + languageName: node + linkType: hard + +"@react-stately/searchfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/searchfield@npm:3.4.1" dependencies: @@ -6653,7 +8413,25 @@ __metadata: languageName: node linkType: hard -"@react-stately/select@npm:^3.3.1, @react-stately/select@npm:^3.5.0": +"@react-stately/select@npm:^3.3.1": + version: 3.3.1 + resolution: "@react-stately/select@npm:3.3.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/collections": ^3.4.3 + "@react-stately/list": ^3.5.3 + "@react-stately/menu": ^3.4.1 + "@react-stately/selection": ^3.10.3 + "@react-stately/utils": ^3.5.1 + "@react-types/select": ^3.6.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 0701cadd640fdea8a3a1c7048e459f701fc8ec9c0ef1fb9692fd70faa5bb7ce23475aba988f57dff90a3db71cbbf8b1ba49edc3df43e744550fbd0e2dcc3575f + languageName: node + linkType: hard + +"@react-stately/select@npm:^3.5.0": version: 3.5.0 resolution: "@react-stately/select@npm:3.5.0" dependencies: @@ -6671,7 +8449,21 @@ __metadata: languageName: node linkType: hard -"@react-stately/selection@npm:^3.10.3, @react-stately/selection@npm:^3.13.0": +"@react-stately/selection@npm:^3.10.3": + version: 3.10.3 + resolution: "@react-stately/selection@npm:3.10.3" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/collections": ^3.4.3 + "@react-stately/utils": ^3.5.1 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: f65af198fa9199bc6bcf76279e2131b605e3ce449cc61d404de34993c81f499d0aba34916e8e8fd867d01ae60786ea3c3b725f3c73153674812bf29e64c6a531 + languageName: node + linkType: hard + +"@react-stately/selection@npm:^3.13.0": version: 3.13.0 resolution: "@react-stately/selection@npm:3.13.0" dependencies: @@ -6701,7 +8493,23 @@ __metadata: languageName: node linkType: hard -"@react-stately/slider@npm:^3.2.1, @react-stately/slider@npm:^3.3.1": +"@react-stately/slider@npm:^3.2.1": + version: 3.2.1 + resolution: "@react-stately/slider@npm:3.2.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-aria/i18n": ^3.6.0 + "@react-aria/utils": ^3.13.3 + "@react-stately/utils": ^3.5.1 + "@react-types/shared": ^3.14.1 + "@react-types/slider": ^3.2.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 3d20eae41b79e481fc45cb4671b17ea20010f199790c963766a58067df18c1b83b41b1394ff3b053b32306cd952bad12331dec09c2a6a6c0c060f336aafee0ca + languageName: node + linkType: hard + +"@react-stately/slider@npm:^3.3.1": version: 3.3.1 resolution: "@react-stately/slider@npm:3.3.1" dependencies: @@ -6717,7 +8525,24 @@ __metadata: languageName: node linkType: hard -"@react-stately/table@npm:^3.4.0, @react-stately/table@npm:^3.9.0": +"@react-stately/table@npm:^3.4.0": + version: 3.4.0 + resolution: "@react-stately/table@npm:3.4.0" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/collections": ^3.4.3 + "@react-stately/grid": ^3.3.1 + "@react-stately/selection": ^3.10.3 + "@react-types/grid": ^3.1.3 + "@react-types/shared": ^3.14.1 + "@react-types/table": ^3.3.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: f3571875fe9978d1f99554d8a31b3af3ced6ac84fde77ed175620f5ce76952833b98ee41b383f5098489c41f504942cedcffe604a4ec6158bbe320267eb70d01 + languageName: node + linkType: hard + +"@react-stately/table@npm:^3.9.0": version: 3.9.0 resolution: "@react-stately/table@npm:3.9.0" dependencies: @@ -6734,7 +8559,21 @@ __metadata: languageName: node linkType: hard -"@react-stately/tabs@npm:^3.2.1, @react-stately/tabs@npm:^3.4.0": +"@react-stately/tabs@npm:^3.2.1": + version: 3.2.1 + resolution: "@react-stately/tabs@npm:3.2.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/list": ^3.5.3 + "@react-stately/utils": ^3.5.1 + "@react-types/tabs": ^3.1.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 593d4ea004ed89156ebf6e2eea401d30e4b06e9eae0f83752550bc5d3d776008577ba0e6baca9791c2e1c0af0f15881a8f95f18923132af08de172cecf097d20 + languageName: node + linkType: hard + +"@react-stately/tabs@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/tabs@npm:3.4.0" dependencies: @@ -6749,7 +8588,21 @@ __metadata: languageName: node linkType: hard -"@react-stately/toggle@npm:^3.4.1, @react-stately/toggle@npm:^3.5.1": +"@react-stately/toggle@npm:^3.4.1": + version: 3.4.1 + resolution: "@react-stately/toggle@npm:3.4.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/utils": ^3.5.1 + "@react-types/checkbox": ^3.3.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 6cc297ac5c840aa20a6d304947a4d869b857c9dc522b7e77cf798f1815ebd5e5ae1f00aeb812fa452fbbfada1069e814b9e1aaf2751b747f875f8b88d88c21fe + languageName: node + linkType: hard + +"@react-stately/toggle@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/toggle@npm:3.5.1" dependencies: @@ -6763,7 +8616,21 @@ __metadata: languageName: node linkType: hard -"@react-stately/tooltip@npm:^3.2.1, @react-stately/tooltip@npm:^3.4.0": +"@react-stately/tooltip@npm:^3.2.1": + version: 3.2.1 + resolution: "@react-stately/tooltip@npm:3.2.1" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/overlays": ^3.4.1 + "@react-stately/utils": ^3.5.1 + "@react-types/tooltip": ^3.2.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: dbb650986c11284dc45b6c0940e3a5aecb7d5e1af92828ae93b4ec1441b580461340033f427523b16f216afb815ebdc491f7aae361e5cd3bcc3dcea1268c76ab + languageName: node + linkType: hard + +"@react-stately/tooltip@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/tooltip@npm:3.4.0" dependencies: @@ -6777,7 +8644,22 @@ __metadata: languageName: node linkType: hard -"@react-stately/tree@npm:^3.3.3, @react-stately/tree@npm:^3.6.0": +"@react-stately/tree@npm:^3.3.3": + version: 3.3.3 + resolution: "@react-stately/tree@npm:3.3.3" + dependencies: + "@babel/runtime": ^7.6.2 + "@react-stately/collections": ^3.4.3 + "@react-stately/selection": ^3.10.3 + "@react-stately/utils": ^3.5.1 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 4e1a94cb478124a2443e84dbf0160dd3a5298e79478336f07003b8c5fcdb26043c65a94439a17315cf00e7f66bf6fd5e3e6fbcb44bced3352554d8f7be94899a + languageName: node + linkType: hard + +"@react-stately/tree@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/tree@npm:3.6.0" dependencies: @@ -6803,6 +8685,17 @@ __metadata: languageName: node linkType: hard +"@react-stately/utils@npm:^3.5.1": + version: 3.5.1 + resolution: "@react-stately/utils@npm:3.5.1" + dependencies: + "@babel/runtime": ^7.6.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: f748331ae393f97b3e6fcccd37b767358f49229520b9500f82ed4c620bff36ef3c01d4ba9679ac7b9d6d78c5f6e711186c98bd0e6482ec27a6fbf26c5d0aa3cc + languageName: node + linkType: hard + "@react-stately/utils@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/utils@npm:3.6.0" @@ -6861,6 +8754,18 @@ __metadata: languageName: node linkType: hard +"@react-types/calendar@npm:^3.0.2": + version: 3.0.2 + resolution: "@react-types/calendar@npm:3.0.2" + dependencies: + "@internationalized/date": ^3.0.1 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: a3fd271d85064837c3b7a495e4048c25da1bbbc21015cfadd970f9959e8c802c9152e25ea772ffd815655e392ce07ce75c688726e70bb4cf6959605bc8257c8e + languageName: node + linkType: hard + "@react-types/calendar@npm:^3.2.0": version: 3.2.0 resolution: "@react-types/calendar@npm:3.2.0" @@ -6873,6 +8778,17 @@ __metadata: languageName: node linkType: hard +"@react-types/checkbox@npm:^3.3.3": + version: 3.3.3 + resolution: "@react-types/checkbox@npm:3.3.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: d1da491ff3bf14f894dbeab5ace3a397ead306d2cc4a820d2a653e038a5628495417feb10a4e07c05dcfce208ae9303c35de7e57d1b21a6b59ca1acca11b80d8 + languageName: node + linkType: hard + "@react-types/checkbox@npm:^3.4.3": version: 3.4.3 resolution: "@react-types/checkbox@npm:3.4.3" @@ -6896,6 +8812,17 @@ __metadata: languageName: node linkType: hard +"@react-types/combobox@npm:^3.5.3": + version: 3.5.3 + resolution: "@react-types/combobox@npm:3.5.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 41e1371f1efa48fe4d56afffeca59d1ed9dad75565c3d67fdf9f6c594529113ce9a8053b95d419682364878a6df0fd6a7178c20e6735778eea2abe74de1ca24f + languageName: node + linkType: hard + "@react-types/combobox@npm:^3.6.1": version: 3.6.1 resolution: "@react-types/combobox@npm:3.6.1" @@ -6907,6 +8834,19 @@ __metadata: languageName: node linkType: hard +"@react-types/datepicker@npm:^3.1.1": + version: 3.1.1 + resolution: "@react-types/datepicker@npm:3.1.1" + dependencies: + "@internationalized/date": ^3.0.1 + "@react-types/overlays": ^3.6.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: a3ab8ae22da8105ffebdebb5c89f212cda4d6f2203f7579cbd733e36afb4d2c7e4f986082becacaa66e7d1b4a99ef109952ddae7760d4bb0685e71d53894e316 + languageName: node + linkType: hard + "@react-types/datepicker@npm:^3.3.0": version: 3.3.0 resolution: "@react-types/datepicker@npm:3.3.0" @@ -6932,6 +8872,17 @@ __metadata: languageName: node linkType: hard +"@react-types/grid@npm:^3.1.3": + version: 3.1.3 + resolution: "@react-types/grid@npm:3.1.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 124b366436160ac7b88368a8be37abf4c703bde3fc1275e720f76d9ee8d0a10825fc5dd314b5eb6bb17b7d0c87091608d9b96d9521329ee5baeb94ab08fa3835 + languageName: node + linkType: hard + "@react-types/grid@npm:^3.1.7": version: 3.1.7 resolution: "@react-types/grid@npm:3.1.7" @@ -6988,6 +8939,18 @@ __metadata: languageName: node linkType: hard +"@react-types/menu@npm:^3.7.1": + version: 3.7.1 + resolution: "@react-types/menu@npm:3.7.1" + dependencies: + "@react-types/overlays": ^3.6.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 349443d1bd23bf64a9af57bef57d8ebfebfc6e82dbcef5cfd8ba778afc998f8dc3cebaae80728e6017b0d12b9e5aaea783254df36dd1b82a048b9e3c0e095795 + languageName: node + linkType: hard + "@react-types/menu@npm:^3.9.0": version: 3.9.0 resolution: "@react-types/menu@npm:3.9.0" @@ -7023,6 +8986,17 @@ __metadata: languageName: node linkType: hard +"@react-types/numberfield@npm:^3.3.3": + version: 3.3.3 + resolution: "@react-types/numberfield@npm:3.3.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: b0f6627157dea0ce8a8fa3434c55bbbc69c4b46c84024d6da6f97091eae45c8b4f9b5b842c2ff82712c1b2e407b4acbd0d476ceda25abd3b9402a0b4573b3b52 + languageName: node + linkType: hard + "@react-types/numberfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/numberfield@npm:3.4.1" @@ -7034,6 +9008,17 @@ __metadata: languageName: node linkType: hard +"@react-types/overlays@npm:^3.6.3": + version: 3.6.3 + resolution: "@react-types/overlays@npm:3.6.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 8688db82adeda13e922f9805a5c9bd9f64e97e91c0ebf32409964e9d661828a4bb31907551dcdcd611807efa9824ff78aa8cb2ee4b0acfab001cbff5572336d4 + languageName: node + linkType: hard + "@react-types/overlays@npm:^3.7.1": version: 3.7.1 resolution: "@react-types/overlays@npm:3.7.1" @@ -7067,6 +9052,17 @@ __metadata: languageName: node linkType: hard +"@react-types/radio@npm:^3.2.3": + version: 3.2.3 + resolution: "@react-types/radio@npm:3.2.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: ce37d92a7e6665a9900b232aae68978bf1c82b4dffd30cc896c6382df7a9bb8a501a30f1b819d0630f9cbf21af3cb6f51de05fbaeaef4a1f250e5d39276eba59 + languageName: node + linkType: hard + "@react-types/radio@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/radio@npm:3.4.1" @@ -7078,6 +9074,18 @@ __metadata: languageName: node linkType: hard +"@react-types/searchfield@npm:^3.3.3": + version: 3.3.3 + resolution: "@react-types/searchfield@npm:3.3.3" + dependencies: + "@react-types/shared": ^3.14.1 + "@react-types/textfield": ^3.5.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: cee59f6ad1da98cc01b81252ef91ebddf0a46df73e4cb3016474c9ad288a0d7b3de2d4607285de97ec23ecaba50391980268ac69b2def096c1fe3a33ecffc686 + languageName: node + linkType: hard + "@react-types/searchfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/searchfield@npm:3.4.1" @@ -7090,6 +9098,17 @@ __metadata: languageName: node linkType: hard +"@react-types/select@npm:^3.6.3": + version: 3.6.3 + resolution: "@react-types/select@npm:3.6.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 472d3086e13ca18857659c5a93e36d5e00c4f1077fd627b16ed93641e6ec39aed77a6cb819e3115616486df3178c2e725aef8dd95cd36fc297819b78111d10b8 + languageName: node + linkType: hard + "@react-types/select@npm:^3.8.0": version: 3.8.0 resolution: "@react-types/select@npm:3.8.0" @@ -7110,7 +9129,16 @@ __metadata: languageName: node linkType: hard -"@react-types/shared@npm:^3.14.1, @react-types/shared@npm:^3.18.0": +"@react-types/shared@npm:^3.14.1": + version: 3.14.1 + resolution: "@react-types/shared@npm:3.14.1" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 117fe230f5a26b7fcaf535c1cfb7c4d42416b0f49d0e0b3436fef2a5851234967908c4e884fc5f2a99a04bee2543543348346a04e1f3f45aaa14c42b6f08491a + languageName: node + linkType: hard + +"@react-types/shared@npm:^3.18.0": version: 3.18.0 resolution: "@react-types/shared@npm:3.18.0" peerDependencies: @@ -7130,6 +9158,17 @@ __metadata: languageName: node linkType: hard +"@react-types/slider@npm:^3.2.1": + version: 3.2.1 + resolution: "@react-types/slider@npm:3.2.1" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 3c64ab2d99fd14debd74181ab4faef43656b274f00443899564e89f3b4b8d9c327184a9c236e4f69c4efc8cba0eca0a0aeae686dcf9a521a7749bab4e0bbdfbb + languageName: node + linkType: hard + "@react-types/slider@npm:^3.5.0": version: 3.5.0 resolution: "@react-types/slider@npm:3.5.0" @@ -7153,6 +9192,18 @@ __metadata: languageName: node linkType: hard +"@react-types/table@npm:^3.3.1": + version: 3.3.1 + resolution: "@react-types/table@npm:3.3.1" + dependencies: + "@react-types/grid": ^3.1.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 1d3e4f8bac6e886944f67c159224893e63ec500f18aaadc74613d9053382c53fd282a7ee9dc21616b7fc0e1291d6ec7ccae87ebca2abdd19e8b371fb8cb46abc + languageName: node + linkType: hard + "@react-types/table@npm:^3.6.0": version: 3.6.0 resolution: "@react-types/table@npm:3.6.0" @@ -7165,6 +9216,17 @@ __metadata: languageName: node linkType: hard +"@react-types/tabs@npm:^3.1.3": + version: 3.1.3 + resolution: "@react-types/tabs@npm:3.1.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 04a95bfb92d2fe44900135bbdd1d256622c51fc90ebecc3374d29eb69bbb77ec13156d39b9fe1806e66b726cf5bbe9ff64822e04e5f3bcacd2429e27c3c260e1 + languageName: node + linkType: hard + "@react-types/tabs@npm:^3.2.1": version: 3.2.1 resolution: "@react-types/tabs@npm:3.2.1" @@ -7187,6 +9249,17 @@ __metadata: languageName: node linkType: hard +"@react-types/textfield@npm:^3.5.3": + version: 3.5.3 + resolution: "@react-types/textfield@npm:3.5.3" + dependencies: + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: f684821edba64e0b525606590800bf2cb6aea98c7304956ed3b2bbcb129ba7734897a9ca1bd056c2f23bf515399fed654071de6a2037942093c2af1c07fad1a9 + languageName: node + linkType: hard + "@react-types/textfield@npm:^3.7.1": version: 3.7.1 resolution: "@react-types/textfield@npm:3.7.1" @@ -7198,6 +9271,18 @@ __metadata: languageName: node linkType: hard +"@react-types/tooltip@npm:^3.2.3": + version: 3.2.3 + resolution: "@react-types/tooltip@npm:3.2.3" + dependencies: + "@react-types/overlays": ^3.6.3 + "@react-types/shared": ^3.14.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + checksum: 5079ee2e561c2b9a7cc6e9dd22d48a26b0d61d79114aff730b7fc8348e199ec1db9e2ef96725f1682ebd5e93bbb223b920aa507f0f9997d8bc3df76f315077a6 + languageName: node + linkType: hard + "@react-types/tooltip@npm:^3.4.0": version: 3.4.0 resolution: "@react-types/tooltip@npm:3.4.0" @@ -7516,7 +9601,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.157": +"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -7530,19 +9615,19 @@ __metadata: linkType: hard "@rocket.chat/css-in-js@npm:next": - version: 0.31.23-dev.157 - resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.157" + version: 0.31.23-dev.103 + resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.103" dependencies: "@emotion/hash": ^0.9.0 - "@rocket.chat/css-supports": ~0.31.23-dev.157 - "@rocket.chat/memo": ~0.31.23-dev.157 - "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.157 + "@rocket.chat/css-supports": ~0.31.23-dev.103 + "@rocket.chat/memo": ~0.31.23-dev.103 + "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.103 stylis: ~4.1.3 - checksum: f573537eced6bd231e897012269c9e256a1ea8d2c9a65bc05806956dd5d2e8ed0586023721754d44ec1c897ab9551da7eae78f48763be92e123b4481036595ae + checksum: 7fea932e9b60d186722f289989aead15fb6d8af2b11f75a44fefa3158c19d953ad789b161599b33bc5ede7c79c4d6de9b3453ca0dd1fd3ec453fe77891df4e55 languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.157": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -7618,9 +9703,9 @@ __metadata: linkType: soft "@rocket.chat/emitter@npm:next": - version: 0.31.23-dev.157 - resolution: "@rocket.chat/emitter@npm:0.31.23-dev.157" - checksum: badd657e886eacd3494a6f40fa67c33c346cbaacc412789ea1703f8ac632f490af2397e4f5c2f0234c3acf5c8fdd112ddb287ba0dd40e392ef4ba74e1ca8cb71 + version: 0.31.23-dev.103 + resolution: "@rocket.chat/emitter@npm:0.31.23-dev.103" + checksum: 5163602cf6793678e1e8b1165fbf4c77a2d24f2536af87d3ceb8464da99ce9bff2ccede931bfd6fde9857890678b82f530b29b088ed41ddac7f4a60363e46f1b languageName: node linkType: hard @@ -7711,21 +9796,33 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-hooks@npm:next, @rocket.chat/fuselage-hooks@npm:~0.32.0-dev.296": - version: 0.32.0-dev.296 - resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.296" +"@rocket.chat/fuselage-hooks@npm:next": + version: 0.32.0-dev.274 + resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.274" dependencies: use-sync-external-store: ~1.2.0 peerDependencies: "@rocket.chat/fuselage-tokens": "*" react: ^17.0.2 - checksum: 85f379b87530bd61961794bba9e2b6c458e4101fa9983f3b643245ce8edfcfff6dbda7ecf1fa88ac7d7df6d927a18f113e0e9cdd50a648e9a061c7ac332fc9df + checksum: 32e0872deb05e8b853aed59cab2cc4b3d3a707f0997009170a12b19d252ca09c203f8139b227c90678c0c06e14228c116ab428b9fba81228d1dab981844d8d54 + languageName: node + linkType: hard + +"@rocket.chat/fuselage-hooks@npm:~0.32.0-dev.242": + version: 0.32.0-dev.242 + resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.242" + dependencies: + use-sync-external-store: ~1.2.0 + peerDependencies: + "@rocket.chat/fuselage-tokens": "*" + react: ^17.0.2 + checksum: f9dcfe53370b55c417668c32b08ddc81dcd6b8fbbc44d7968109906b3053985b34ee7d1ecdbea895b1d2eaaae7960fe2559cf0a6136799ea4725b986cb69895e languageName: node linkType: hard "@rocket.chat/fuselage-polyfills@npm:next": - version: 0.31.23-dev.157 - resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.157" + version: 0.31.23-dev.103 + resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.103" dependencies: "@juggle/resize-observer": ^3.4.0 clipboard-polyfill: ^3.0.3 @@ -7733,13 +9830,13 @@ __metadata: focus-visible: ^5.2.0 focus-within-polyfill: ^5.2.1 new-event-polyfill: ^1.0.1 - checksum: 14da84dc80002d99736d3a148602cc5d82f1c830fdfef84b3db4aa11822c42a2c96e43a3aecb62a4eb222c750399d9f9349b224601c2f2011d375b5be76a3bf3 + checksum: b4dbda8530718a9d585fbc38ed5d6efcb29d8df64982a7434117e11fc75df39c94faa666fd269cb1139d2431349e23fbb21dc5d7c100abb31f18eee27079d56c languageName: node linkType: hard "@rocket.chat/fuselage-toastbar@npm:next": - version: 0.32.0-dev.357 - resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.357" + version: 0.32.0-dev.303 + resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.303" peerDependencies: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" @@ -7747,11 +9844,18 @@ __metadata: "@rocket.chat/styled": "*" react: ^17.0.2 react-dom: ^17.0.2 - checksum: 4a9c81ee42928b500fccec76542b0d812d9e79ab4ac9a89e8ee77ce19db45db78242218a799214f9248f1e04c636934ee70d44febc089ece38c35ac21837c101 + checksum: 479f5c50d1f6ce7a7e4cc4bbd0909f3bc1c3e83e3a9fdeb806d2b0540a17c46e30f8e72b816c954257411bd404f693d83bdf9b2499ce13cee2426d03c1dbec76 languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.333": +"@rocket.chat/fuselage-tokens@npm:next": + version: 0.32.0-dev.280 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.280" + checksum: d8c7537512a7950ff6f0f3e23b3d6a2b82c0caea42d5b6e5599257fe2319cbd6f53a229c61413b025b20f576521aca67d9cba378f15e19edddae11a55133672a + languageName: node + linkType: hard + +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.333": version: 0.32.0-dev.333 resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.333" checksum: dd19009e75e6154361be566ecea5f328902fdf25a10fdcc9ecd18076e424dd5f7d1a4b4c56b7679fe28e4a4ff77e32107d2f83a2c9c98f968ef6ff18f5de95db @@ -7921,9 +10025,9 @@ __metadata: linkType: soft "@rocket.chat/icons@npm:next": - version: 0.32.0-dev.365 - resolution: "@rocket.chat/icons@npm:0.32.0-dev.365" - checksum: 6323cadb2931582cbae4ec4cc271ae881a82915c9a1b0958a9a52c82ed5a5c3dad82699f3e0e22e580bc12695413e772eeb0a6cac670ab0c7863dd73d2f44cb0 + version: 0.32.0-dev.362 + resolution: "@rocket.chat/icons@npm:0.32.0-dev.362" + checksum: 1c2096913e154d0db68b8ccc933294486ff06e250983809ce165f1497df05deec8b5165b47dbefdd013013b4cdf4a3a20f2ac191155b629e5ec4a7bcab5d8870 languageName: node linkType: hard @@ -7941,14 +10045,14 @@ __metadata: linkType: soft "@rocket.chat/layout@npm:next": - version: 0.32.0-dev.266 - resolution: "@rocket.chat/layout@npm:0.32.0-dev.266" + version: 0.32.0-dev.213 + resolution: "@rocket.chat/layout@npm:0.32.0-dev.213" peerDependencies: "@rocket.chat/fuselage": "*" react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: cbecb1564ea77578071dd2571ae3b004485232f035291c02f1b00fbee6c4ae12955c91a3feeb11b153f6b33ea5662b5691d4b7436cd2c8f40a36a8d990100a62 + checksum: 6a7e069f669fe7201e12ffb287064fe724e69294010b6cc426d4c5936f7e2c93cfe9ae92ee480e0ce7864cd5af37b5f9163dbdcefb850571adc3242ed897a5f1 languageName: node linkType: hard @@ -8049,19 +10153,19 @@ __metadata: linkType: soft "@rocket.chat/logo@npm:next": - version: 0.32.0-dev.333 - resolution: "@rocket.chat/logo@npm:0.32.0-dev.333" + version: 0.32.0-dev.279 + resolution: "@rocket.chat/logo@npm:0.32.0-dev.279" dependencies: - "@rocket.chat/fuselage-hooks": ~0.32.0-dev.296 - "@rocket.chat/styled": ~0.31.23-dev.157 + "@rocket.chat/fuselage-hooks": ~0.32.0-dev.242 + "@rocket.chat/styled": ~0.31.23-dev.103 peerDependencies: react: 17.0.2 react-dom: 17.0.2 - checksum: 502931f4aea0730e446aa17e22a881c9a19cbd15da4be5ec26c4fed51a4400fea9365ec5880c5fef3dc0f8a7ace6f1c1204f644a0b217b8eb1658de92214d1e6 + checksum: a79a80a89111633f2c325ace46dc951fe67df94eb192a4ed691e274e19e951f7b22a9a301108f3db4daaac7667f8b9f5f2535a123a0ab72e94796358678acaeb languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.157": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -8069,18 +10173,18 @@ __metadata: linkType: hard "@rocket.chat/memo@npm:next": - version: 0.31.23-dev.157 - resolution: "@rocket.chat/memo@npm:0.31.23-dev.157" - checksum: e1939d45e2759522463424c0ad876b5e7570a5c7bce561ee4f5e166b16778f794d54a1e2028dc90b1602ff2d20f32ae889b905e894b68af4df6de6844e1fb216 + version: 0.31.23-dev.103 + resolution: "@rocket.chat/memo@npm:0.31.23-dev.103" + checksum: 44b91bb7e02ffbeb2b094e5f283a1ddc3ca205f6ff01f5d044911b8cfc690b7e1472ee9f296de46bc84dcf2eb64d4c314813aa74d2076ec170ec1321f43145a6 languageName: node linkType: hard "@rocket.chat/message-parser@npm:next": - version: 0.32.0-dev.331 - resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.331" + version: 0.32.0-dev.296 + resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.296" dependencies: tldts: ~5.7.112 - checksum: dfb9fc430eba71c32b2ca1ea6357a3d42623a08dc41d9e9d23235fb0c71a939a0255ef3d2860df8f43d89fb23a34cccd0d66c470e01eaec4804b62410dba3808 + checksum: 2a40090b0b5b94e531da3bd1433c6f60b98f19b85eb9ca81db8706fcf360d8245c97fde2257a6c39e9be7fa4a630eede5abf679847ef3c923594c7156f8eb334 languageName: node linkType: hard @@ -8560,8 +10664,8 @@ __metadata: linkType: soft "@rocket.chat/onboarding-ui@npm:next": - version: 0.32.0-dev.383 - resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.383" + version: 0.32.0-dev.329 + resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.329" dependencies: i18next: ~21.6.16 react-hook-form: ~7.27.1 @@ -8576,7 +10680,7 @@ __metadata: react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: 427d4f4f6265a551e623129a118edc2170e689af12f68bb2c2c3e1b52905fdcacd950f34f7dba59173eef9ece4c21860d8366b76c3dc21a7c23f281bc74b3b9a + checksum: 3118c3f3bb91db6e30748a1bf031221b2459b611c260446adf5bafcc9e38fa600a8a3366567a88183b2e93286b538233cb585f0d414b53f02e2eb48b8b2ba3ad languageName: node linkType: hard @@ -8853,22 +10957,22 @@ __metadata: linkType: soft "@rocket.chat/string-helpers@npm:next": - version: 0.31.23-dev.157 - resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.157" - checksum: 41d49dcc57aaea77f71928f7c5da8c68deebafe4b33718f98d6af4427c7df6e212bf0dca0703d74d9cf7fb24c65ec8215e37f8240544754963e0a83e5d7f62f6 + version: 0.31.23-dev.103 + resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.103" + checksum: b505564152613d5871dae226b67458d69fbacf2ddeb3f6d10cf81124ccccfd75ad60133bc772db6bf20f4b17f7cfe09fc0ea9c846c2f519b031ce22d0da6688e languageName: node linkType: hard "@rocket.chat/styled@npm:next": - version: 0.31.23-dev.157 - resolution: "@rocket.chat/styled@npm:0.31.23-dev.157" + version: 0.31.23-dev.103 + resolution: "@rocket.chat/styled@npm:0.31.23-dev.103" dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.157 - checksum: ab4aaa77b8570111d37233954322e036316bde3bf65cf781e242ca936ec84f4dca4fed7d91cbd7ff4b2aab329f812d7887003f721a9c72fd4544bbc9fbf342af + "@rocket.chat/css-in-js": ~0.31.23-dev.103 + checksum: aadeb2ffe37e01d694012ec49d301a9a8960b82377fc1daa2c332b657414c4c52839010d3f8ce5114312065b8da0cc41b70bdc93fa1916169c53d333d56428bc languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.157": +"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.157": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: @@ -8877,7 +10981,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/stylis-logical-props-middleware@npm:^0.31.23, @rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.157": +"@rocket.chat/stylis-logical-props-middleware@npm:^0.31.23, @rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.103": version: 0.31.23 resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23" dependencies: @@ -9011,9 +11115,9 @@ __metadata: linkType: soft "@rocket.chat/ui-kit@npm:next": - version: 0.32.0-dev.318 - resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.318" - checksum: 4cb64edb3942635e23ba904da95eb5b742b1cadeb89c1aad7fb2acc60b25886cf83cfcd9c785f6a57959e1e3bcd713ac8014ffe45b70bc6d40b1651a206fe6bc + version: 0.32.0-dev.294 + resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.294" + checksum: cf58890f3fbcfdff3df9436a33c4aa333de34369f89902107dee74c50c11b8af793c9c80a8df5046ffcabf0fd90111f31a6fc2030876e838e43ec4c6a5703fc4 languageName: node linkType: hard @@ -11009,7 +13113,33 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7, @types/babel__core@npm:^7.1.14, @types/babel__core@npm:~7.20.1": +"@types/babel__core@npm:^7": + version: 7.20.0 + resolution: "@types/babel__core@npm:7.20.0" + dependencies: + "@babel/parser": ^7.20.7 + "@babel/types": ^7.20.7 + "@types/babel__generator": "*" + "@types/babel__template": "*" + "@types/babel__traverse": "*" + checksum: 49b601a0a7637f1f387442c8156bd086cfd10ff4b82b0e1994e73a6396643b5435366fb33d6b604eade8467cca594ef97adcbc412aede90bb112ebe88d0ad6df + languageName: node + linkType: hard + +"@types/babel__core@npm:^7.1.14": + version: 7.1.20 + resolution: "@types/babel__core@npm:7.1.20" + dependencies: + "@babel/parser": ^7.20.7 + "@babel/types": ^7.20.7 + "@types/babel__generator": "*" + "@types/babel__template": "*" + "@types/babel__traverse": "*" + checksum: a09c4f0456552547a5b8a5a009a3daec4d362f622168f8e08bda0ded2da0a65ab0b1642e23c433b3616721f5701dc34a996c5bde5baeaea53eda98f438043f2c + languageName: node + linkType: hard + +"@types/babel__core@npm:~7.20.1": version: 7.20.1 resolution: "@types/babel__core@npm:7.20.1" dependencies: @@ -11146,7 +13276,14 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:*, @types/chai@npm:^4.3.5": +"@types/chai@npm:*": + version: 4.3.1 + resolution: "@types/chai@npm:4.3.1" + checksum: 2ee246b76c469cd620a7a1876a73bc597074361b67d547b4bd96a0c1adb43597ede2d8589ab626192e14349d83cbb646cc11e2c179eeeb43ff11596de94d82c4 + languageName: node + linkType: hard + +"@types/chai@npm:^4.3.5": version: 4.3.5 resolution: "@types/chai@npm:4.3.5" checksum: c8f26a88c6b5b53a3275c7f5ff8f107028e3cbb9ff26795fff5f3d9dea07106a54ce9e2dce5e40347f7c4cc35657900aaf0c83934a25a1ae12e61e0f5516e431 @@ -11289,30 +13426,40 @@ __metadata: languageName: node linkType: hard -"@types/eslint@npm:*, @types/eslint@npm:^8.40.2, @types/eslint@npm:~8.40.2": - version: 8.40.2 - resolution: "@types/eslint@npm:8.40.2" +"@types/eslint@npm:*": + version: 8.37.0 + resolution: "@types/eslint@npm:8.37.0" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: a4780e45e677e3af21c44a900846996cb6d9ae8f71d51940942a047163ae93a05444392c005f491ed46aa169f3b25f8be125ab42c5d8bdb571154bf62a7c828a + checksum: 06d3b3fba12004294591b5c7a52e3cec439472195da54e096076b1f2ddfbb8a445973b9681046dd530a6ac31eca502f635abc1e3ce37d03513089358e6f822ee languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": - version: 1.0.1 - resolution: "@types/estree@npm:1.0.1" - checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d +"@types/eslint@npm:^8.40.2, @types/eslint@npm:~8.40.2": + version: 8.40.2 + resolution: "@types/eslint@npm:8.40.2" + dependencies: + "@types/estree": "*" + "@types/json-schema": "*" + checksum: a4780e45e677e3af21c44a900846996cb6d9ae8f71d51940942a047163ae93a05444392c005f491ed46aa169f3b25f8be125ab42c5d8bdb571154bf62a7c828a languageName: node linkType: hard -"@types/estree@npm:^0.0.51": +"@types/estree@npm:*, @types/estree@npm:^0.0.51": version: 0.0.51 resolution: "@types/estree@npm:0.0.51" checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 languageName: node linkType: hard +"@types/estree@npm:^1.0.0": + version: 1.0.1 + resolution: "@types/estree@npm:1.0.1" + checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d + languageName: node + linkType: hard + "@types/express-rate-limit@npm:^5.1.3": version: 5.1.3 resolution: "@types/express-rate-limit@npm:5.1.3" @@ -11322,7 +13469,18 @@ __metadata: languageName: node linkType: hard -"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": +"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.18": + version: 4.17.31 + resolution: "@types/express-serve-static-core@npm:4.17.31" + dependencies: + "@types/node": "*" + "@types/qs": "*" + "@types/range-parser": "*" + checksum: 009bfbe1070837454a1056aa710d0390ee5fb8c05dfe5a1691cc3e2ca88dc256f80e1ca27cb51a978681631d2f6431bfc9ec352ea46dd0c6eb183d0170bde5df + languageName: node + linkType: hard + +"@types/express-serve-static-core@npm:^4.17.33": version: 4.17.35 resolution: "@types/express-serve-static-core@npm:4.17.35" dependencies: @@ -11334,7 +13492,19 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.17, @types/express@npm:^4.17.8": +"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.8": + version: 4.17.13 + resolution: "@types/express@npm:4.17.13" + dependencies: + "@types/body-parser": "*" + "@types/express-serve-static-core": ^4.17.18 + "@types/qs": "*" + "@types/serve-static": "*" + checksum: 12a2a0e6c4b993fc0854bec665906788aea0d8ee4392389d7a98a5de1eefdd33c9e1e40a91f3afd274011119c506f7b4126acb97fae62ae20b654974d44cba12 + languageName: node + linkType: hard + +"@types/express@npm:^4.17.17": version: 4.17.17 resolution: "@types/express@npm:4.17.17" dependencies: @@ -11503,13 +13673,13 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*, @types/jest@npm:^29.5.1, @types/jest@npm:^29.5.2, @types/jest@npm:~29.5.2": - version: 29.5.2 - resolution: "@types/jest@npm:29.5.2" +"@types/jest@npm:*": + version: 29.5.0 + resolution: "@types/jest@npm:29.5.0" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: 7d205599ea3cccc262bad5cc173d3242d6bf8138c99458509230e4ecef07a52d6ddcde5a1dbd49ace655c0af51d2dbadef3748697292ea4d86da19d9e03e19c0 + checksum: cd877e5c56d299cceb8bfdcbb1a77723c706750dd3c3bc47403bc3599b8faff590a3b009c68bb5b11bf7a8c77d1fb01de5e124329b4a08e65f1cdda28b0ecdb8 languageName: node linkType: hard @@ -11523,6 +13693,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^29.5.1, @types/jest@npm:^29.5.2, @types/jest@npm:~29.5.2": + version: 29.5.2 + resolution: "@types/jest@npm:29.5.2" + dependencies: + expect: ^29.0.0 + pretty-format: ^29.0.0 + checksum: 7d205599ea3cccc262bad5cc173d3242d6bf8138c99458509230e4ecef07a52d6ddcde5a1dbd49ace655c0af51d2dbadef3748697292ea4d86da19d9e03e19c0 + languageName: node + linkType: hard + "@types/jquery@npm:*": version: 3.5.14 resolution: "@types/jquery@npm:3.5.14" @@ -11655,7 +13835,14 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:*, @types/lodash@npm:^4.14.167, @types/lodash@npm:^4.14.195": +"@types/lodash@npm:*, @types/lodash@npm:^4.14.167": + version: 4.14.182 + resolution: "@types/lodash@npm:4.14.182" + checksum: 7dd137aa9dbabd632408bd37009d984655164fa1ecc3f2b6eb94afe35bf0a5852cbab6183148d883e9c73a958b7fec9a9bcf7c8e45d41195add6a18c34958209 + languageName: node + linkType: hard + +"@types/lodash@npm:^4.14.195": version: 4.14.195 resolution: "@types/lodash@npm:4.14.195" checksum: 39b75ca635b3fa943d17d3d3aabc750babe4c8212485a4df166fe0516e39288e14b0c60afc6e21913cc0e5a84734633c71e617e2bd14eaa1cf51b8d7799c432e @@ -11814,21 +14001,44 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^14.0.10 || ^16.0.0, @types/node@npm:^14.14.20 || ^16.0.0, @types/node@npm:^16.18.36": - version: 16.18.36 - resolution: "@types/node@npm:16.18.36" - checksum: a9d138fa1269079c60daad6984713dc0b713983f8b34a83edbc6d7957b2e38beab9b2598c9fe99f19d073e20bc212a18aaf82eabdc23ef64dce7d2089a9aab2a +"@types/node@npm:^14.0.10 || ^16.0.0, @types/node@npm:^14.14.20 || ^16.0.0": + version: 16.11.39 + resolution: "@types/node@npm:16.11.39" + checksum: bc97b9773ac6b3194800f990b349fad7f66c6126dacef59291b10a2c8b6813d6f67f947b7e12a6c9952790f7065d576fe38355b8fe034a6af60f317cfc570f69 languageName: node linkType: hard -"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37, @types/node@npm:^14.18.51": +"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37": + version: 14.18.21 + resolution: "@types/node@npm:14.18.21" + checksum: 4ed35b76609647a4e36a194702e31cdda9ed42174ddaf7937bc5498984e98a99e8a42ea895ea17dd9c5ec18080112c29ab670c34f90eb9f7a4703b85b31e34fa + languageName: node + linkType: hard + +"@types/node@npm:^14.18.51": version: 14.18.51 resolution: "@types/node@npm:14.18.51" checksum: 0960a31d2ac605763fe79c8edcee3cb48257d345ce417c019d84ff5d8cd92dd0937674814ab3f169346b4259c29f640556006bcb2c54cfb3e63fa0cf728d320e languageName: node linkType: hard -"@types/nodemailer@npm:*, @types/nodemailer@npm:^6.4.8": +"@types/node@npm:^16.18.36": + version: 16.18.36 + resolution: "@types/node@npm:16.18.36" + checksum: a9d138fa1269079c60daad6984713dc0b713983f8b34a83edbc6d7957b2e38beab9b2598c9fe99f19d073e20bc212a18aaf82eabdc23ef64dce7d2089a9aab2a + languageName: node + linkType: hard + +"@types/nodemailer@npm:*": + version: 6.4.7 + resolution: "@types/nodemailer@npm:6.4.7" + dependencies: + "@types/node": "*" + checksum: dc2a33a89135e04a5bea4921e8645e8453b90e3c3b05f0646f05071c5951ab697ea49ea1e503a690f04cb0a6abfc54967325c5a4036356793cfbb64ba64fb141 + languageName: node + linkType: hard + +"@types/nodemailer@npm:^6.4.8": version: 6.4.8 resolution: "@types/nodemailer@npm:6.4.8" dependencies: @@ -12004,7 +14214,16 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:<18.0.0, @types/react-dom@npm:^17.0.20, @types/react-dom@npm:~17.0.20": +"@types/react-dom@npm:<18.0.0": + version: 17.0.17 + resolution: "@types/react-dom@npm:17.0.17" + dependencies: + "@types/react": ^17 + checksum: 23caf98aa03e968811560f92a2c8f451694253ebe16b670929b24eaf0e7fa62ba549abe9db0ac028a9d8a9086acd6ab9c6c773f163fa21224845edbc00ba6232 + languageName: node + linkType: hard + +"@types/react-dom@npm:^17.0.20, @types/react-dom@npm:~17.0.20": version: 17.0.20 resolution: "@types/react-dom@npm:17.0.20" dependencies: @@ -12013,7 +14232,16 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.2.5": +"@types/react-dom@npm:^18.0.0": + version: 18.0.10 + resolution: "@types/react-dom@npm:18.0.10" + dependencies: + "@types/react": "*" + checksum: ff8282d5005a0b1cd95fb65bf79d3d8485e4cfe2aaf052129033a178684b940014a3f4536bc20d573f8a01cf4c6f4770c74988cef7c2b5cac3041d9f172647e3 + languageName: node + linkType: hard + +"@types/react-dom@npm:^18.2.5": version: 18.2.5 resolution: "@types/react-dom@npm:18.2.5" dependencies: @@ -12034,7 +14262,18 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^17, @types/react@npm:^17.0.62, @types/react@npm:~17.0.62": +"@types/react@npm:*, @types/react@npm:^17": + version: 17.0.58 + resolution: "@types/react@npm:17.0.58" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: 4eaf32b86c43f388c681e34a00921c508dd88a1d1022aebfadc5fe802b7c5bed863de1a17eed31e43ca2d65222952dfe79a022055a0e6e4e1ad89fc5a42ec05e + languageName: node + linkType: hard + +"@types/react@npm:^17.0.62, @types/react@npm:~17.0.62": version: 17.0.62 resolution: "@types/react@npm:17.0.62" dependencies: @@ -12260,7 +14499,16 @@ __metadata: languageName: node linkType: hard -"@types/testing-library__jest-dom@npm:^5.9.1, @types/testing-library__jest-dom@npm:~5.14.6": +"@types/testing-library__jest-dom@npm:^5.9.1": + version: 5.14.5 + resolution: "@types/testing-library__jest-dom@npm:5.14.5" + dependencies: + "@types/jest": "*" + checksum: dcb05416758fe88c1f4f3aa97b4699fcb46a5ed8f53c6b81721e66155452a48caf12ecb97dfdfd4130678e65efd66b9fca0ac434b3d63affec84842a84a6bf38 + languageName: node + linkType: hard + +"@types/testing-library__jest-dom@npm:~5.14.6": version: 5.14.6 resolution: "@types/testing-library__jest-dom@npm:5.14.6" dependencies: @@ -12413,7 +14661,16 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.1, @types/ws@npm:^8.5.5": +"@types/ws@npm:^8.5.1": + version: 8.5.4 + resolution: "@types/ws@npm:8.5.4" + dependencies: + "@types/node": "*" + checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1 + languageName: node + linkType: hard + +"@types/ws@npm:^8.5.5": version: 8.5.5 resolution: "@types/ws@npm:8.5.5" dependencies: @@ -12516,6 +14773,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:5.58.0": + version: 5.58.0 + resolution: "@typescript-eslint/scope-manager@npm:5.58.0" + dependencies: + "@typescript-eslint/types": 5.58.0 + "@typescript-eslint/visitor-keys": 5.58.0 + checksum: f0d3df5cc3c461fe63ef89ad886b53c239cc7c1d9061d83d8a9d9c8e087e5501eac84bebff8a954728c17ccea191f235686373d54d2b8b6370af2bcf2b18e062 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/scope-manager@npm:5.60.0" @@ -12543,6 +14810,20 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/types@npm:5.52.0" + checksum: 018940d61aebf7cf3f7de1b9957446e2ea01f08fe950bef4788c716a3a88f7c42765fe7d80152b0d0428fcd4bd3ace2dfa8c459ba1c59d9a84e951642180f869 + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:5.58.0": + version: 5.58.0 + resolution: "@typescript-eslint/types@npm:5.58.0" + checksum: 8622a73d73220c4a7111537825f488c0271272032a1d4e129dc722bc6e8b3ec84f64469b2ca3b8dae7da3a9c18953ce1449af51f5f757dad60835eb579ad1d2c + languageName: node + linkType: hard + "@typescript-eslint/types@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/types@npm:5.60.0" @@ -12550,6 +14831,41 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.52.0" + dependencies: + "@typescript-eslint/types": 5.52.0 + "@typescript-eslint/visitor-keys": 5.52.0 + debug: ^4.3.4 + globby: ^11.1.0 + is-glob: ^4.0.3 + semver: ^7.3.7 + tsutils: ^3.21.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 67d396907fee3d6894e26411a5098a37f07e5d50343189e6361ff7db91c74a7ffe2abd630d11f14c2bda1f4af13edf52b80b11cbccb55b44079c7cec14c9e108 + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:5.58.0": + version: 5.58.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.58.0" + dependencies: + "@typescript-eslint/typescript-estree": 5.52.0 + "@typescript-eslint/utils": 5.52.0 + debug: ^4.3.4 + tsutils: ^3.21.0 + peerDependencies: + eslint: "*" + peerDependenciesMeta: + typescript: + optional: true + checksum: 51b668ec858db0c040a71dff526273945cee4ba5a9b240528d503d02526685882d900cf071c6636a4d9061ed3fd4a7274f7f1a23fba55c4b48b143344b4009c7 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/typescript-estree@npm:5.60.0" @@ -12568,7 +14884,25 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.60.0, @typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.45.0, @typescript-eslint/utils@npm:^5.58.0": +"@typescript-eslint/utils@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/utils@npm:5.52.0" + dependencies: + "@eslint-community/eslint-utils": ^4.2.0 + "@types/json-schema": ^7.0.9 + "@types/semver": ^7.3.12 + "@typescript-eslint/scope-manager": 5.58.0 + "@typescript-eslint/types": 5.58.0 + "@typescript-eslint/typescript-estree": 5.58.0 + eslint-scope: ^5.1.1 + semver: ^7.3.7 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 01906be5262ece36537e9d586e4d2d4791e05752a9354bcb42b1f5bf965f53daa13309c61c3dff5e201ea28c298e4e01cf0c93738afa0099fea0da3b1d8cb3a5 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/utils@npm:5.60.0" dependencies: @@ -12586,6 +14920,44 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.45.0, @typescript-eslint/utils@npm:^5.58.0": + version: 5.58.0 + resolution: "@typescript-eslint/utils@npm:5.58.0" + dependencies: + "@eslint-community/eslint-utils": ^4.2.0 + "@types/json-schema": ^7.0.9 + "@types/semver": ^7.3.12 + "@typescript-eslint/scope-manager": 5.58.0 + "@typescript-eslint/types": 5.58.0 + "@typescript-eslint/typescript-estree": 5.58.0 + eslint-scope: ^5.1.1 + semver: ^7.3.7 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: c618ae67963ecf96b1492c09afaeb363f542f0d6780bcac4af3c26034e3b20034666b2d523aa94821df813aafb57a0b150a7d5c2224fe8257452ad1de2237a58 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:5.52.0": + version: 5.52.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.52.0" + dependencies: + "@typescript-eslint/types": 5.52.0 + eslint-visitor-keys: ^3.3.0 + checksum: 33b44f0cd35b7b47f34e89d52e47b8d8200f55af306b22db4de104d79f65907458ea022e548f50d966e32fea150432ac9c1ae65b3001b0ad2ac8a17c0211f370 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:5.58.0": + version: 5.58.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.58.0" + dependencies: + "@typescript-eslint/types": 5.58.0 + eslint-visitor-keys: ^3.3.0 + checksum: ab2d1f37660559954c840429ef78bbf71834063557e3e68e435005b4987970b9356fdf217ead53f7a57f66f5488dc478062c5c44bf17053a8bf041733539b98f + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.60.0": version: 5.60.0 resolution: "@typescript-eslint/visitor-keys@npm:5.60.0" @@ -12635,6 +15007,16 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/ast@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/ast@npm:1.11.1" + dependencies: + "@webassemblyjs/helper-numbers": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + checksum: 1eee1534adebeece635362f8e834ae03e389281972611408d64be7895fc49f48f98fddbbb5339bf8a72cb101bcb066e8bca3ca1bf1ef47dadf89def0395a8d87 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.6, @webassemblyjs/ast@npm:^1.11.5": version: 1.11.6 resolution: "@webassemblyjs/ast@npm:1.11.6" @@ -12656,6 +15038,13 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/floating-point-hex-parser@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.1" + checksum: b8efc6fa08e4787b7f8e682182d84dfdf8da9d9c77cae5d293818bc4a55c1f419a87fa265ab85252b3e6c1fd323d799efea68d825d341a7c365c64bc14750e97 + languageName: node + linkType: hard + "@webassemblyjs/floating-point-hex-parser@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.6" @@ -12670,6 +15059,13 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/helper-api-error@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-api-error@npm:1.11.1" + checksum: 0792813f0ed4a0e5ee0750e8b5d0c631f08e927f4bdfdd9fe9105dc410c786850b8c61bff7f9f515fdfb149903bec3c976a1310573a4c6866a94d49bc7271959 + languageName: node + linkType: hard + "@webassemblyjs/helper-api-error@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-api-error@npm:1.11.6" @@ -12684,6 +15080,13 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/helper-buffer@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-buffer@npm:1.11.1" + checksum: a337ee44b45590c3a30db5a8b7b68a717526cf967ada9f10253995294dbd70a58b2da2165222e0b9830cd4fc6e4c833bf441a721128d1fe2e9a7ab26b36003ce + languageName: node + linkType: hard + "@webassemblyjs/helper-buffer@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-buffer@npm:1.11.6" @@ -12723,6 +15126,17 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/helper-numbers@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-numbers@npm:1.11.1" + dependencies: + "@webassemblyjs/floating-point-hex-parser": 1.11.1 + "@webassemblyjs/helper-api-error": 1.11.1 + "@xtuc/long": 4.2.2 + checksum: 44d2905dac2f14d1e9b5765cf1063a0fa3d57295c6d8930f6c59a36462afecc6e763e8a110b97b342a0f13376166c5d41aa928e6ced92e2f06b071fd0db59d3a + languageName: node + linkType: hard + "@webassemblyjs/helper-numbers@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-numbers@npm:1.11.6" @@ -12734,6 +15148,13 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/helper-wasm-bytecode@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.1" + checksum: eac400113127832c88f5826bcc3ad1c0db9b3dbd4c51a723cfdb16af6bfcbceb608170fdaac0ab7731a7e18b291be7af68a47fcdb41cfe0260c10857e7413d97 + languageName: node + linkType: hard + "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6" @@ -12748,6 +15169,18 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/helper-wasm-section@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-buffer": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/wasm-gen": 1.11.1 + checksum: 617696cfe8ecaf0532763162aaf748eb69096fb27950219bb87686c6b2e66e11cd0614d95d319d0ab1904bc14ebe4e29068b12c3e7c5e020281379741fe4bedf + languageName: node + linkType: hard + "@webassemblyjs/helper-wasm-section@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.6" @@ -12772,6 +15205,15 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/ieee754@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/ieee754@npm:1.11.1" + dependencies: + "@xtuc/ieee754": ^1.2.0 + checksum: 23a0ac02a50f244471631802798a816524df17e56b1ef929f0c73e3cde70eaf105a24130105c60aff9d64a24ce3b640dad443d6f86e5967f922943a7115022ec + languageName: node + linkType: hard + "@webassemblyjs/ieee754@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/ieee754@npm:1.11.6" @@ -12790,6 +15232,15 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/leb128@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/leb128@npm:1.11.1" + dependencies: + "@xtuc/long": 4.2.2 + checksum: 33ccc4ade2f24de07bf31690844d0b1ad224304ee2062b0e464a610b0209c79e0b3009ac190efe0e6bd568b0d1578d7c3047fc1f9d0197c92fc061f56224ff4a + languageName: node + linkType: hard + "@webassemblyjs/leb128@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/leb128@npm:1.11.6" @@ -12808,6 +15259,13 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/utf8@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/utf8@npm:1.11.1" + checksum: 972c5cfc769d7af79313a6bfb96517253a270a4bf0c33ba486aa43cac43917184fb35e51dfc9e6b5601548cd5931479a42e42c89a13bb591ffabebf30c8a6a0b + languageName: node + linkType: hard + "@webassemblyjs/utf8@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/utf8@npm:1.11.6" @@ -12822,6 +15280,22 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-edit@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-edit@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-buffer": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/helper-wasm-section": 1.11.1 + "@webassemblyjs/wasm-gen": 1.11.1 + "@webassemblyjs/wasm-opt": 1.11.1 + "@webassemblyjs/wasm-parser": 1.11.1 + "@webassemblyjs/wast-printer": 1.11.1 + checksum: 6d7d9efaec1227e7ef7585a5d7ff0be5f329f7c1c6b6c0e906b18ed2e9a28792a5635e450aca2d136770d0207225f204eff70a4b8fd879d3ac79e1dcc26dbeb9 + languageName: node + linkType: hard + "@webassemblyjs/wasm-edit@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/wasm-edit@npm:1.9.0" @@ -12854,6 +15328,19 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-gen@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-gen@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/ieee754": 1.11.1 + "@webassemblyjs/leb128": 1.11.1 + "@webassemblyjs/utf8": 1.11.1 + checksum: 1f6921e640293bf99fb16b21e09acb59b340a79f986c8f979853a0ae9f0b58557534b81e02ea2b4ef11e929d946708533fd0693c7f3712924128fdafd6465f5b + languageName: node + linkType: hard + "@webassemblyjs/wasm-gen@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wasm-gen@npm:1.11.6" @@ -12880,6 +15367,18 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-opt@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-opt@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-buffer": 1.11.1 + "@webassemblyjs/wasm-gen": 1.11.1 + "@webassemblyjs/wasm-parser": 1.11.1 + checksum: 21586883a20009e2b20feb67bdc451bbc6942252e038aae4c3a08e6f67b6bae0f5f88f20bfc7bd0452db5000bacaf5ab42b98cf9aa034a6c70e9fc616142e1db + languageName: node + linkType: hard + "@webassemblyjs/wasm-opt@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wasm-opt@npm:1.11.6" @@ -12904,6 +15403,20 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wasm-parser@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wasm-parser@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/helper-api-error": 1.11.1 + "@webassemblyjs/helper-wasm-bytecode": 1.11.1 + "@webassemblyjs/ieee754": 1.11.1 + "@webassemblyjs/leb128": 1.11.1 + "@webassemblyjs/utf8": 1.11.1 + checksum: 1521644065c360e7b27fad9f4bb2df1802d134dd62937fa1f601a1975cde56bc31a57b6e26408b9ee0228626ff3ba1131ae6f74ffb7d718415b6528c5a6dbfc2 + languageName: node + linkType: hard + "@webassemblyjs/wasm-parser@npm:1.11.6, @webassemblyjs/wasm-parser@npm:^1.11.5": version: 1.11.6 resolution: "@webassemblyjs/wasm-parser@npm:1.11.6" @@ -12946,6 +15459,16 @@ __metadata: languageName: node linkType: hard +"@webassemblyjs/wast-printer@npm:1.11.1": + version: 1.11.1 + resolution: "@webassemblyjs/wast-printer@npm:1.11.1" + dependencies: + "@webassemblyjs/ast": 1.11.1 + "@xtuc/long": 4.2.2 + checksum: f15ae4c2441b979a3b4fce78f3d83472fb22350c6dc3fd34bfe7c3da108e0b2360718734d961bba20e7716cb8578e964b870da55b035e209e50ec9db0378a3f7 + languageName: node + linkType: hard + "@webassemblyjs/wast-printer@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wast-printer@npm:1.11.6" @@ -13000,14 +15523,14 @@ __metadata: languageName: node linkType: hard -"@xmldom/xmldom@npm:0.8.7": +"@xmldom/xmldom@npm:0.8.7, @xmldom/xmldom@npm:^0.8.5": version: 0.8.7 resolution: "@xmldom/xmldom@npm:0.8.7" checksum: 593d4429c2281ee7799adcb6ff8604b68cf30ce0721537e3e380287b423e67c7ac197d90987f932b4fd3febc409ded8435706e7f90fbba6e22e08740477341d1 languageName: node linkType: hard -"@xmldom/xmldom@npm:^0.8.5, @xmldom/xmldom@npm:^0.8.8": +"@xmldom/xmldom@npm:^0.8.8": version: 0.8.8 resolution: "@xmldom/xmldom@npm:0.8.8" checksum: 5f5fc0482fcc599f62e3009516932a265e00f1bb2093fe2c76f3f8d9bfebdd13246f48d4132c9b301c7a573f0fa8712e56aa747dce75b179c2b73f1dde7b5f42 @@ -13102,6 +15625,15 @@ __metadata: languageName: node linkType: hard +"acorn-import-assertions@npm:^1.7.6": + version: 1.8.0 + resolution: "acorn-import-assertions@npm:1.8.0" + peerDependencies: + acorn: ^8 + checksum: 5c4cf7c850102ba7ae0eeae0deb40fb3158c8ca5ff15c0bca43b5c47e307a1de3d8ef761788f881343680ea374631ae9e9615ba8876fee5268dbe068c98bcba6 + languageName: node + linkType: hard + "acorn-import-assertions@npm:^1.9.0": version: 1.9.0 resolution: "acorn-import-assertions@npm:1.9.0" @@ -13152,7 +15684,16 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1, acorn@npm:^8.8.2": +"acorn@npm:^8.1.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1": + version: 8.8.2 + resolution: "acorn@npm:8.8.2" + bin: + acorn: bin/acorn + checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 + languageName: node + linkType: hard + +"acorn@npm:^8.8.2": version: 8.9.0 resolution: "acorn@npm:8.9.0" bin: @@ -13304,7 +15845,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.11.0, ajv@npm:^8.8.0": +"ajv@npm:^8.0.0, ajv@npm:^8.8.0": version: 8.12.0 resolution: "ajv@npm:8.12.0" dependencies: @@ -13316,6 +15857,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^8.0.1, ajv@npm:^8.11.0": + version: 8.11.0 + resolution: "ajv@npm:8.11.0" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef + languageName: node + linkType: hard + "alphanum-sort@npm:^1.0.0": version: 1.0.2 resolution: "alphanum-sort@npm:1.0.2" @@ -13371,7 +15924,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:4.1.1": +"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 @@ -13385,7 +15938,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:^4.1.1, ansi-colors@npm:^4.1.3": +"ansi-colors@npm:^4.1.3": version: 4.1.3 resolution: "ansi-colors@npm:4.1.3" checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e @@ -13703,7 +16256,19 @@ __metadata: languageName: node linkType: hard -"args@npm:^5.0.1, args@npm:^5.0.3": +"args@npm:^5.0.1": + version: 5.0.1 + resolution: "args@npm:5.0.1" + dependencies: + camelcase: 5.0.0 + chalk: 2.4.2 + leven: 2.1.0 + mri: 1.1.4 + checksum: 51e2a05f32d15b8e292f000e6b232118df61b8f4fd446b17bb4e99df9ab47fe2c4a01924d7f967a6f08e82f9c19be277b08ed22bceff058aca849144ef8efed3 + languageName: node + linkType: hard + +"args@npm:^5.0.3": version: 5.0.3 resolution: "args@npm:5.0.3" dependencies: @@ -14343,6 +16908,19 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs2@npm:^0.3.3": + version: 0.3.3 + resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" + dependencies: + "@babel/compat-data": ^7.17.7 + "@babel/helper-define-polyfill-provider": ^0.3.3 + semver: ^6.1.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7db3044993f3dddb3cc3d407bc82e640964a3bfe22de05d90e1f8f7a5cb71460011ab136d3c03c6c1ba428359ebf635688cd6205e28d0469bba221985f5c6179 + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs2@npm:^0.4.3": version: 0.4.3 resolution: "babel-plugin-polyfill-corejs2@npm:0.4.3" @@ -14368,6 +16946,18 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs3@npm:^0.6.0": + version: 0.6.0 + resolution: "babel-plugin-polyfill-corejs3@npm:0.6.0" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.3.3 + core-js-compat: ^3.25.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 470bb8c59f7c0912bd77fe1b5a2e72f349b3f65bbdee1d60d6eb7e1f4a085c6f24b2dd5ab4ac6c2df6444a96b070ef6790eccc9edb6a2668c60d33133bfb62c6 + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs3@npm:^0.8.1": version: 0.8.1 resolution: "babel-plugin-polyfill-corejs3@npm:0.8.1" @@ -14380,6 +16970,17 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-regenerator@npm:^0.4.1": + version: 0.4.1 + resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.3.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ab0355efbad17d29492503230387679dfb780b63b25408990d2e4cf421012dae61d6199ddc309f4d2409ce4e9d3002d187702700dd8f4f8770ebbba651ed066c + languageName: node + linkType: hard + "babel-plugin-polyfill-regenerator@npm:^0.5.0": version: 0.5.0 resolution: "babel-plugin-polyfill-regenerator@npm:0.5.0" @@ -14842,7 +17443,7 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.20.2, body-parser@npm:^1.19.0, body-parser@npm:^1.20.2": +"body-parser@npm:1.20.2, body-parser@npm:^1.20.2": version: 1.20.2 resolution: "body-parser@npm:1.20.2" dependencies: @@ -14862,6 +17463,26 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:^1.19.0": + version: 1.20.0 + resolution: "body-parser@npm:1.20.0" + dependencies: + bytes: 3.1.2 + content-type: ~1.0.4 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.10.3 + raw-body: 2.5.1 + type-is: ~1.6.18 + unpipe: 1.0.0 + checksum: 12fffdeac82fe20dddcab7074215d5156e7d02a69ae90cbe9fee1ca3efa2f28ef52097cbea76685ee0a1509c71d85abd0056a08e612c09077cad6277a644cf88 + languageName: node + linkType: hard + "bonjour-service@npm:^1.0.11": version: 1.1.1 resolution: "bonjour-service@npm:1.1.1" @@ -15116,7 +17737,21 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.3, browserslist@npm:^4.21.5": +"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.3": + version: 4.21.3 + resolution: "browserslist@npm:4.21.3" + dependencies: + caniuse-lite: ^1.0.30001370 + electron-to-chromium: ^1.4.202 + node-releases: ^2.0.6 + update-browserslist-db: ^1.0.5 + bin: + browserslist: cli.js + checksum: ff512a7bcca1c530e2854bbdfc7be2791d0fb524097a6340e56e1d5924164c7e4e0a9b070de04cdc4c149d15cb4d4275cb7c626ebbce954278a2823aaad2452a + languageName: node + linkType: hard + +"browserslist@npm:^4.21.5": version: 4.21.9 resolution: "browserslist@npm:4.21.9" dependencies: @@ -15623,7 +18258,14 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001503": +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001370": + version: 1.0.30001464 + resolution: "caniuse-lite@npm:1.0.30001464" + checksum: 67cdee102c1660d62d7b9dbd4740bb7af096236618f2509fd2e0039d50db5f02fb87c21d90b6d573fdcf50deaf3c84503d009e871502b5c221d0ba1dec18ba11 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001503": version: 1.0.30001503 resolution: "caniuse-lite@npm:1.0.30001503" checksum: cd5f0af37655ff71ec4ab3c49124d75e0b8b68de625d07ea80e9a82329e616b5203d5dad6865192653be9da50081c06878f081ab069dac0be35adf29aa1599cd @@ -15729,7 +18371,22 @@ __metadata: languageName: node linkType: hard -"chai@npm:>1.9.0, chai@npm:^4.3.7": +"chai@npm:>1.9.0": + version: 4.3.6 + resolution: "chai@npm:4.3.6" + dependencies: + assertion-error: ^1.1.0 + check-error: ^1.0.2 + deep-eql: ^3.0.1 + get-func-name: ^2.0.0 + loupe: ^2.3.1 + pathval: ^1.1.1 + type-detect: ^4.0.5 + checksum: acff93fd537f96d4a4d62dd83810285dffcfccb5089e1bf2a1205b28ec82d93dff551368722893cf85004282df10ee68802737c33c90c5493957ed449ed7ce71 + languageName: node + linkType: hard + +"chai@npm:^4.3.7": version: 4.3.7 resolution: "chai@npm:4.3.7" dependencies: @@ -16013,13 +18670,20 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0": +"ci-info@npm:^3.1.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 languageName: node linkType: hard +"ci-info@npm:^3.2.0": + version: 3.3.0 + resolution: "ci-info@npm:3.3.0" + checksum: c3d86fe374938ecda5093b1ba39acb535d8309185ba3f23587747c6a057e63f45419b406d880304dbc0e1d72392c9a33e42fe9a1e299209bc0ded5efaa232b66 + languageName: node + linkType: hard + "cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": version: 1.0.4 resolution: "cipher-base@npm:1.0.4" @@ -16424,7 +19088,14 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.10, colorette@npm:^2.0.14, colorette@npm:^2.0.20, colorette@npm:^2.0.7": +"colorette@npm:^2.0.10, colorette@npm:^2.0.14, colorette@npm:^2.0.7": + version: 2.0.19 + resolution: "colorette@npm:2.0.19" + checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 + languageName: node + linkType: hard + +"colorette@npm:^2.0.20": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d @@ -16702,7 +19373,14 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.4, content-type@npm:~1.0.4, content-type@npm:~1.0.5": +"content-type@npm:^1.0.4, content-type@npm:~1.0.4": + version: 1.0.4 + resolution: "content-type@npm:1.0.4" + checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0 + languageName: node + linkType: hard + +"content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 @@ -16810,7 +19488,16 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.30.1, core-js-compat@npm:^3.30.2, core-js-compat@npm:^3.8.1": +"core-js-compat@npm:^3.25.1, core-js-compat@npm:^3.8.1": + version: 3.25.1 + resolution: "core-js-compat@npm:3.25.1" + dependencies: + browserslist: ^4.21.3 + checksum: 34dbec657adc2f660f4cd701709c9c5e27cbd608211c65df09458f80f3e357b9492ba1c5173e17cca72d889dcc6da01268cadf88fb407cf1726e76d301c6143e + languageName: node + linkType: hard + +"core-js-compat@npm:^3.30.1, core-js-compat@npm:^3.30.2": version: 3.31.0 resolution: "core-js-compat@npm:3.31.0" dependencies: @@ -17038,7 +19725,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:3.1.5": +"cross-fetch@npm:3.1.5, cross-fetch@npm:^3.1.5": version: 3.1.5 resolution: "cross-fetch@npm:3.1.5" dependencies: @@ -17047,7 +19734,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.0.4, cross-fetch@npm:^3.1.5": +"cross-fetch@npm:^3.0.4": version: 3.1.6 resolution: "cross-fetch@npm:3.1.6" dependencies: @@ -17841,7 +20528,14 @@ __metadata: languageName: node linkType: hard -"decode-uri-component@npm:^0.2.0, decode-uri-component@npm:^0.2.2": +"decode-uri-component@npm:^0.2.0": + version: 0.2.0 + resolution: "decode-uri-component@npm:0.2.0" + checksum: f3749344ab9305ffcfe4bfe300e2dbb61fc6359e2b736812100a3b1b6db0a5668cba31a05e4b45d4d63dbf1a18dfa354cd3ca5bb3ededddabb8cd293f4404f94 + languageName: node + linkType: hard + +"decode-uri-component@npm:^0.2.2": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 @@ -17936,6 +20630,15 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^3.0.1": + version: 3.0.1 + resolution: "deep-eql@npm:3.0.1" + dependencies: + type-detect: ^4.0.0 + checksum: 4f4c9fb79eb994fb6e81d4aa8b063adc40c00f831588aa65e20857d5d52f15fb23034a6576ecf886f7ff6222d5ae42e71e9b7d57113e0715b1df7ea1e812b125 + languageName: node + linkType: hard + "deep-eql@npm:^4.1.2": version: 4.1.3 resolution: "deep-eql@npm:4.1.3" @@ -18436,7 +21139,14 @@ __metadata: languageName: node linkType: hard -"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": +"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": + version: 2.2.0 + resolution: "domelementtype@npm:2.2.0" + checksum: 24cb386198640cd58aa36f8c987f2ea61859929106d06ffcc8f547e70cb2ed82a6dc56dcb8252b21fba1f1ea07df6e4356d60bfe57f77114ca1aed6828362629 + languageName: node + linkType: hard + +"domelementtype@npm:^2.3.0": version: 2.3.0 resolution: "domelementtype@npm:2.3.0" checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 @@ -18702,6 +21412,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.4.202": + version: 1.4.249 + resolution: "electron-to-chromium@npm:1.4.249" + checksum: 830a35a157af7ae226f1528d727e369bb13f53bc7a4edefdf718651ace09d7d7b4bd7b70d33b5018b8eff6cf99ee58409b6c4140cd6d56350c1966f280ac5c93 + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.4.431": version: 1.4.433 resolution: "electron-to-chromium@npm:1.4.433" @@ -18890,6 +21607,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:^5.10.0": + version: 5.10.0 + resolution: "enhanced-resolve@npm:5.10.0" + dependencies: + graceful-fs: ^4.2.4 + tapable: ^2.2.0 + checksum: 0bb9830704db271610f900e8d79d70a740ea16f251263362b0c91af545576d09fe50103496606c1300a05e588372d6f9780a9bc2e30ce8ef9b827ec8f44687ff + languageName: node + linkType: hard + "enhanced-resolve@npm:^5.15.0": version: 5.15.0 resolution: "enhanced-resolve@npm:5.15.0" @@ -18923,13 +21650,20 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0, entities@npm:^4.4.0": +"entities@npm:^4.2.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 languageName: node linkType: hard +"entities@npm:^4.4.0": + version: 4.4.0 + resolution: "entities@npm:4.4.0" + checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2 + languageName: node + linkType: hard + "entities@npm:~2.0.0": version: 2.0.3 resolution: "entities@npm:2.0.3" @@ -19063,6 +21797,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^0.9.0": + version: 0.9.3 + resolution: "es-module-lexer@npm:0.9.3" + checksum: 84bbab23c396281db2c906c766af58b1ae2a1a2599844a504df10b9e8dc77ec800b3211fdaa133ff700f5703d791198807bba25d9667392d27a5e9feda344da8 + languageName: node + linkType: hard + "es-module-lexer@npm:^1.2.1": version: 1.3.0 resolution: "es-module-lexer@npm:1.3.0" @@ -19118,7 +21859,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.17.5, esbuild@npm:^0.17.6": +"esbuild@npm:^0.17.5": version: 0.17.19 resolution: "esbuild@npm:0.17.19" dependencies: @@ -19195,6 +21936,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.17.6": + version: 0.17.18 + resolution: "esbuild@npm:0.17.18" + dependencies: + "@esbuild/android-arm": 0.17.18 + "@esbuild/android-arm64": 0.17.18 + "@esbuild/android-x64": 0.17.18 + "@esbuild/darwin-arm64": 0.17.18 + "@esbuild/darwin-x64": 0.17.18 + "@esbuild/freebsd-arm64": 0.17.18 + "@esbuild/freebsd-x64": 0.17.18 + "@esbuild/linux-arm": 0.17.18 + "@esbuild/linux-arm64": 0.17.18 + "@esbuild/linux-ia32": 0.17.18 + "@esbuild/linux-loong64": 0.17.18 + "@esbuild/linux-mips64el": 0.17.18 + "@esbuild/linux-ppc64": 0.17.18 + "@esbuild/linux-riscv64": 0.17.18 + "@esbuild/linux-s390x": 0.17.18 + "@esbuild/linux-x64": 0.17.18 + "@esbuild/netbsd-x64": 0.17.18 + "@esbuild/openbsd-x64": 0.17.18 + "@esbuild/sunos-x64": 0.17.18 + "@esbuild/win32-arm64": 0.17.18 + "@esbuild/win32-ia32": 0.17.18 + "@esbuild/win32-x64": 0.17.18 + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 900b333f649fd89804216fb61fb5a0ffadc6dc37a2ec3b5981b588f72821676ea649a7c0ec785f0dbe6e774080b084c8af5f6ee7adbc1b138faf2a8c35e2c69c + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -19526,7 +22344,14 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1": +"eslint-visitor-keys@npm:^3.3.0": + version: 3.4.0 + resolution: "eslint-visitor-keys@npm:3.4.0" + checksum: 33159169462d3989321a1ec1e9aaaf6a24cc403d5d347e9886d1b5bfe18ffa1be73bdc6203143a28a606b142b1af49787f33cff0d6d0813eb5f2e8d2e1a6043c + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.4.1": version: 3.4.1 resolution: "eslint-visitor-keys@npm:3.4.1" checksum: f05121d868202736b97de7d750847a328fcfa8593b031c95ea89425333db59676ac087fa905eba438d0a3c5769632f828187e0c1a0d271832a2153c1d3661c2c @@ -19757,7 +22582,14 @@ __metadata: languageName: node linkType: hard -"eventemitter2@npm:^6.3.1, eventemitter2@npm:^6.4.9": +"eventemitter2@npm:^6.3.1": + version: 6.4.5 + resolution: "eventemitter2@npm:6.4.5" + checksum: 84504f9cf0cc30205cdd46783fe9df3733435e5097f13070b678023110b5ef07847651808ae280cd94c42cd5976880211c7a40321a8ff8fa56f7c5f9c5c11960 + languageName: node + linkType: hard + +"eventemitter2@npm:^6.4.9": version: 6.4.9 resolution: "eventemitter2@npm:6.4.9" checksum: be59577c1e1c35509c7ba0e2624335c35bbcfd9485b8a977384c6cc6759341ea1a98d3cb9dbaa5cea4fff9b687e504504e3f9c2cc1674cf3bd8a43a7c74ea3eb @@ -20176,7 +23008,20 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": +"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": + version: 3.2.11 + resolution: "fast-glob@npm:3.2.11" + dependencies: + "@nodelib/fs.stat": ^2.0.2 + "@nodelib/fs.walk": ^1.2.3 + glob-parent: ^5.1.2 + merge2: ^1.3.0 + micromatch: ^4.0.4 + checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 + languageName: node + linkType: hard + +"fast-glob@npm:^3.2.12": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" dependencies: @@ -20745,7 +23590,17 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.2": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9": + version: 1.15.1 + resolution: "follow-redirects@npm:1.15.1" + peerDependenciesMeta: + debug: + optional: true + checksum: 6aa4e3e3cdfa3b9314801a1cd192ba756a53479d9d8cca65bf4db3a3e8834e62139245cd2f9566147c8dfe2efff1700d3e6aefd103de4004a7b99985e71dd533 + languageName: node + linkType: hard + +"follow-redirects@npm:^1.15.2": version: 1.15.2 resolution: "follow-redirects@npm:1.15.2" peerDependenciesMeta: @@ -21024,6 +23879,13 @@ __metadata: languageName: node linkType: hard +"fs-monkey@npm:^1.0.3": + version: 1.0.3 + resolution: "fs-monkey@npm:1.0.3" + checksum: cf50804833f9b88a476911ae911fe50f61a98d986df52f890bd97e7262796d023698cb2309fa9b74fdd8974f04315b648748a0a8ee059e7d5257b293bfc409c0 + languageName: node + linkType: hard + "fs-monkey@npm:^1.0.4": version: 1.0.4 resolution: "fs-monkey@npm:1.0.4" @@ -21875,7 +24737,14 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.5": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 @@ -27179,7 +30048,16 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.1.2, memfs@npm:^3.2.2, memfs@npm:^3.4.3": +"memfs@npm:^3.1.2, memfs@npm:^3.4.3": + version: 3.5.0 + resolution: "memfs@npm:3.5.0" + dependencies: + fs-monkey: ^1.0.3 + checksum: 8427db6c3644eeb9119b7a74b232d9a6178d018878acce6f05bd89d95e28b1073c9eeb00127131b0613b07a003e2e7b15b482f9004e548fe06a0aba7aa02515c + languageName: node + linkType: hard + +"memfs@npm:^3.2.2": version: 3.5.3 resolution: "memfs@npm:3.5.3" dependencies: @@ -28019,7 +30897,16 @@ __metadata: languageName: node linkType: hard -"moment-timezone@npm:*, moment-timezone@npm:^0.5.43, moment-timezone@npm:^0.5.x, moment-timezone@npm:~0.5.43": +"moment-timezone@npm:*, moment-timezone@npm:^0.5.x": + version: 0.5.40 + resolution: "moment-timezone@npm:0.5.40" + dependencies: + moment: ">= 2.9.0" + checksum: 6f6be5412b37fd937bb143efe74bf65b2c3f115fd967a6dc13b717a126ed6dd198bff6db6e179d69a089e20ac03ce7622c6b5598dd585005195554487a91b528 + languageName: node + linkType: hard + +"moment-timezone@npm:^0.5.43, moment-timezone@npm:~0.5.43": version: 0.5.43 resolution: "moment-timezone@npm:0.5.43" dependencies: @@ -28028,7 +30915,7 @@ __metadata: languageName: node linkType: hard -"moment@npm:^2.10.2, moment@npm:^2.29.1, moment@npm:^2.29.4": +"moment@npm:>= 2.9.0, moment@npm:^2.10.2, moment@npm:^2.29.1, moment@npm:^2.29.4": version: 2.29.4 resolution: "moment@npm:2.29.4" checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e @@ -28276,7 +31163,16 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.1, nanoid@npm:^3.3.6": +"nanoid@npm:^3.3.1, nanoid@npm:^3.3.4": + version: 3.3.4 + resolution: "nanoid@npm:3.3.4" + bin: + nanoid: bin/nanoid.cjs + checksum: 2fddd6dee994b7676f008d3ffa4ab16035a754f4bb586c61df5a22cf8c8c94017aadd360368f47d653829e0569a92b129979152ff97af23a558331e47e37cd9c + languageName: node + linkType: hard + +"nanoid@npm:^3.3.6": version: 3.3.6 resolution: "nanoid@npm:3.3.6" bin: @@ -28539,7 +31435,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:2.6.7": +"node-fetch@npm:2.6.7, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" dependencies: @@ -28553,7 +31449,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.11, node-fetch@npm:^2.6.7": +"node-fetch@npm:^2.6.11": version: 2.6.11 resolution: "node-fetch@npm:2.6.11" dependencies: @@ -28697,6 +31593,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.6": + version: 2.0.6 + resolution: "node-releases@npm:2.0.6" + checksum: e86a926dc9fbb3b41b4c4a89d998afdf140e20a4e8dbe6c0a807f7b2948b42ea97d7fd3ad4868041487b6e9ee98409829c6e4d84a734a4215dff060a7fbeb4bf + languageName: node + linkType: hard + "node-rsa@npm:^1.1.1": version: 1.1.1 resolution: "node-rsa@npm:1.1.1" @@ -31200,7 +34103,17 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.0, postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6": +"postcss-selector-parser@npm:^6.0.0, postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.6": + version: 6.0.10 + resolution: "postcss-selector-parser@npm:6.0.10" + dependencies: + cssesc: ^3.0.0 + util-deprecate: ^1.0.2 + checksum: 46afaa60e3d1998bd7adf6caa374baf857cc58d3ff944e29459c9a9e4680a7fe41597bd5b755fc81d7c388357e9bf67c0251d047c640a09f148e13606b8a8608 + languageName: node + linkType: hard + +"postcss-selector-parser@npm:^6.0.4": version: 6.0.13 resolution: "postcss-selector-parser@npm:6.0.13" dependencies: @@ -31301,7 +34214,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.2.15, postcss@npm:^8.3.11, postcss@npm:^8.4.14, postcss@npm:^8.4.23, postcss@npm:~8.4.24": +"postcss@npm:^8.2.15, postcss@npm:~8.4.24": version: 8.4.24 resolution: "postcss@npm:8.4.24" dependencies: @@ -31312,6 +34225,28 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.3.11, postcss@npm:^8.4.14": + version: 8.4.16 + resolution: "postcss@npm:8.4.16" + dependencies: + nanoid: ^3.3.4 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: 10eee25efd77868036403858577da0cefaf2e0905feeaba5770d5438ccdddba3d01cba8063e96b8aac4c6daa0ed413dd5ae0554a433a3c4db38df1d134cffc1f + languageName: node + linkType: hard + +"postcss@npm:^8.4.23": + version: 8.4.23 + resolution: "postcss@npm:8.4.23" + dependencies: + nanoid: ^3.3.6 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: 8bb9d1b2ea6e694f8987d4f18c94617971b2b8d141602725fedcc2222fdc413b776a6e1b969a25d627d7b2681ca5aabb56f59e727ef94072e1b6ac8412105a2f + languageName: node + linkType: hard + "postis@npm:^2.2.0": version: 2.2.0 resolution: "postis@npm:2.2.0" @@ -31892,6 +34827,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.10.3": + version: 6.10.3 + resolution: "qs@npm:6.10.3" + dependencies: + side-channel: ^1.0.4 + checksum: 0fac5e6c7191d0295a96d0e83c851aeb015df7e990e4d3b093897d3ac6c94e555dbd0a599739c84d7fa46d7fee282d94ba76943983935cf33bba6769539b8019 + languageName: node + linkType: hard + "qs@npm:6.11.0, qs@npm:^6.10.0, qs@npm:^6.10.3, qs@npm:^6.7.0, qs@npm:^6.9.4, qs@npm:^6.9.6": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -32187,7 +35131,19 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.5.2, raw-body@npm:^2.2.0": +"raw-body@npm:2.5.1, raw-body@npm:^2.2.0": + version: 2.5.1 + resolution: "raw-body@npm:2.5.1" + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e + languageName: node + linkType: hard + +"raw-body@npm:2.5.2": version: 2.5.2 resolution: "raw-body@npm:2.5.2" dependencies: @@ -32211,7 +35167,20 @@ __metadata: languageName: node linkType: hard -"rc-scrollbars@npm:^1.1.5, rc-scrollbars@npm:^1.1.6": +"rc-scrollbars@npm:^1.1.5": + version: 1.1.5 + resolution: "rc-scrollbars@npm:1.1.5" + dependencies: + dom-css: ^2.1.0 + raf: ^3.4.1 + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: d2b3710868f70ed7c154768bd03825b543d7804ef1c95086ee9cd851f66a046e1b4dbc9e153f9797e1706a11d263db4a645eff485d16bf1c095754e6e771f5ad + languageName: node + linkType: hard + +"rc-scrollbars@npm:^1.1.6": version: 1.1.6 resolution: "rc-scrollbars@npm:1.1.6" dependencies: @@ -33695,7 +36664,21 @@ __metadata: languageName: unknown linkType: soft -"rollup@npm:^3.2.5, rollup@npm:^3.21.0": +"rollup@npm:^3.2.5": + version: 3.21.3 + resolution: "rollup@npm:3.21.3" + dependencies: + fsevents: ~2.3.2 + dependenciesMeta: + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 430c0c66f1480586ccea77b190524efb6a3af3da5308c7bc77eca3160c3f387c9a56ebb3eef5bfb24f683764bdfdd3dafdea175d215e4b875bbde752619bec12 + languageName: node + linkType: hard + +"rollup@npm:^3.21.0": version: 3.23.0 resolution: "rollup@npm:3.23.0" dependencies: @@ -34003,7 +36986,18 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": + version: 3.1.1 + resolution: "schema-utils@npm:3.1.1" + dependencies: + "@types/json-schema": ^7.0.8 + ajv: ^6.12.5 + ajv-keywords: ^3.5.2 + checksum: fb73f3d759d43ba033c877628fe9751620a26879f6301d3dbeeb48cf2a65baec5cdf99da65d1bf3b4ff5444b2e59cbe4f81c2456b5e0d2ba7d7fd4aed5da29ce + languageName: node + linkType: hard + +"schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" dependencies: @@ -34141,7 +37135,18 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.2, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2": +"semver@npm:7.x, semver@npm:^7.2, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": + version: 7.3.7 + resolution: "semver@npm:7.3.7" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 + languageName: node + linkType: hard + +"semver@npm:^7.5.2": version: 7.5.2 resolution: "semver@npm:7.5.2" dependencies: @@ -34213,7 +37218,7 @@ __metadata: languageName: node linkType: hard -"serialize-javascript@npm:6.0.0": +"serialize-javascript@npm:6.0.0, serialize-javascript@npm:^6.0.0": version: 6.0.0 resolution: "serialize-javascript@npm:6.0.0" dependencies: @@ -36234,6 +39239,28 @@ __metadata: languageName: node linkType: hard +"terser-webpack-plugin@npm:^5.1.3": + version: 5.3.5 + resolution: "terser-webpack-plugin@npm:5.3.5" + dependencies: + "@jridgewell/trace-mapping": ^0.3.14 + jest-worker: ^27.4.5 + schema-utils: ^3.1.1 + serialize-javascript: ^6.0.0 + terser: ^5.14.1 + peerDependencies: + webpack: ^5.1.0 + peerDependenciesMeta: + "@swc/core": + optional: true + esbuild: + optional: true + uglify-js: + optional: true + checksum: 611c7b38d6fa0213dc03f48da9efe29c7edd098fc128a64905f7c9b61af8e7c36c13113d46b50be19ee2b8378442f4e1b8b4ddac9bba2cb73499ed32fc0e18f4 + languageName: node + linkType: hard + "terser@npm:^4.1.2, terser@npm:^4.6.3": version: 4.8.0 resolution: "terser@npm:4.8.0" @@ -36247,7 +39274,7 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.10.0, terser@npm:^5.16.8, terser@npm:^5.3.4": +"terser@npm:^5.10.0, terser@npm:^5.16.8": version: 5.18.0 resolution: "terser@npm:5.18.0" dependencies: @@ -36261,6 +39288,20 @@ __metadata: languageName: node linkType: hard +"terser@npm:^5.14.1, terser@npm:^5.3.4": + version: 5.14.2 + resolution: "terser@npm:5.14.2" + dependencies: + "@jridgewell/source-map": ^0.3.2 + acorn: ^8.5.0 + commander: ^2.20.0 + source-map-support: ~0.5.20 + bin: + terser: bin/terser + checksum: cabb50a640d6c2cfb351e4f43dc7bf7436f649755bb83eb78b2cacda426d5e0979bd44e6f92d713f3ca0f0866e322739b9ced888ebbce6508ad872d08de74fcc + languageName: node + linkType: hard + "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -36420,13 +39461,20 @@ __metadata: languageName: node linkType: hard -"tiny-invariant@npm:^1.0.6, tiny-invariant@npm:^1.2.0": +"tiny-invariant@npm:^1.0.6": version: 1.3.1 resolution: "tiny-invariant@npm:1.3.1" checksum: 872dbd1ff20a21303a2fd20ce3a15602cfa7fcf9b228bd694a52e2938224313b5385a1078cb667ed7375d1612194feaca81c4ecbe93121ca1baebe344de4f84c languageName: node linkType: hard +"tiny-invariant@npm:^1.2.0": + version: 1.2.0 + resolution: "tiny-invariant@npm:1.2.0" + checksum: e09a718a7c4a499ba592cdac61f015d87427a0867ca07f50c11fd9b623f90cdba18937b515d4a5e4f43dac92370498d7bdaee0d0e7a377a61095e02c4a92eade + languageName: node + linkType: hard + "tinykeys@npm:^1.4.0": version: 1.4.0 resolution: "tinykeys@npm:1.4.0" @@ -36849,7 +39897,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.3": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": + version: 2.5.0 + resolution: "tslib@npm:2.5.0" + checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 + languageName: node + linkType: hard + +"tslib@npm:^2.5.3": version: 2.5.3 resolution: "tslib@npm:2.5.3" checksum: 88902b309afaf83259131c1e13da1dceb0ad1682a213143a1346a649143924d78cf3760c448b84d796938fd76127183894f8d85cbb3bf9c4fddbfcc140c0003c @@ -37629,6 +40684,20 @@ __metadata: languageName: node linkType: hard +"update-browserslist-db@npm:^1.0.5": + version: 1.0.9 + resolution: "update-browserslist-db@npm:1.0.9" + dependencies: + escalade: ^3.1.1 + picocolors: ^1.0.0 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + browserslist-lint: cli.js + checksum: f625899b236f6a4d7f62b56be1b8da230c5563d1fef84d3ef148f2e1a3f11a5a4b3be4fd7e3703e51274c116194017775b10afb4de09eb2c0d09d36b90f1f578 + languageName: node + linkType: hard + "update-check@npm:1.5.2": version: 1.5.2 resolution: "update-check@npm:1.5.2" @@ -38191,7 +41260,7 @@ __metadata: languageName: node linkType: hard -"vm2@npm:^3.9.19, vm2@npm:^3.9.8": +"vm2@npm:^3.9.19": version: 3.9.19 resolution: "vm2@npm:3.9.19" dependencies: @@ -38203,6 +41272,18 @@ __metadata: languageName: node linkType: hard +"vm2@npm:^3.9.8": + version: 3.9.17 + resolution: "vm2@npm:3.9.17" + dependencies: + acorn: ^8.7.0 + acorn-walk: ^8.2.0 + bin: + vm2: bin/vm2 + checksum: 9a03740a40ab2be5e3348a95fb31512da1a3c85318febb07e5299fa103ff05bcd7b6f458211fa38a1281dc27beccd04ff90355fc1d34fe2ee6ca10d0bb8c6f35 + languageName: node + linkType: hard + "void-elements@npm:3.1.0": version: 3.1.0 resolution: "void-elements@npm:3.1.0" @@ -38636,7 +41717,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:>=4.0.0 <6.0.0, webpack@npm:>=4.43.0 <6.0.0, webpack@npm:^5.9.0": +"webpack@npm:>=4.0.0 <6.0.0": version: 5.88.0 resolution: "webpack@npm:5.88.0" dependencies: @@ -38673,6 +41754,80 @@ __metadata: languageName: node linkType: hard +"webpack@npm:>=4.43.0 <6.0.0": + version: 5.74.0 + resolution: "webpack@npm:5.74.0" + dependencies: + "@types/eslint-scope": ^3.7.3 + "@types/estree": ^0.0.51 + "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/wasm-edit": 1.11.1 + "@webassemblyjs/wasm-parser": 1.11.1 + acorn: ^8.7.1 + acorn-import-assertions: ^1.7.6 + browserslist: ^4.14.5 + chrome-trace-event: ^1.0.2 + enhanced-resolve: ^5.10.0 + es-module-lexer: ^0.9.0 + eslint-scope: 5.1.1 + events: ^3.2.0 + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.2.9 + json-parse-even-better-errors: ^2.3.1 + loader-runner: ^4.2.0 + mime-types: ^2.1.27 + neo-async: ^2.6.2 + schema-utils: ^3.1.0 + tapable: ^2.1.1 + terser-webpack-plugin: ^5.1.3 + watchpack: ^2.4.0 + webpack-sources: ^3.2.3 + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: 320c41369a75051b19e18c63f408b3dcc481852e992f83d311771c5ec0f05f2946385e8ebef62030cf3587f0a3d2f12779ffdb191569a966847289ba7313f946 + languageName: node + linkType: hard + +"webpack@npm:^5.9.0": + version: 5.87.0 + resolution: "webpack@npm:5.87.0" + dependencies: + "@types/eslint-scope": ^3.7.3 + "@types/estree": ^1.0.0 + "@webassemblyjs/ast": ^1.11.5 + "@webassemblyjs/wasm-edit": ^1.11.5 + "@webassemblyjs/wasm-parser": ^1.11.5 + acorn: ^8.7.1 + acorn-import-assertions: ^1.9.0 + browserslist: ^4.14.5 + chrome-trace-event: ^1.0.2 + enhanced-resolve: ^5.15.0 + es-module-lexer: ^1.2.1 + eslint-scope: 5.1.1 + events: ^3.2.0 + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.2.9 + json-parse-even-better-errors: ^2.3.1 + loader-runner: ^4.2.0 + mime-types: ^2.1.27 + neo-async: ^2.6.2 + schema-utils: ^3.2.0 + tapable: ^2.1.1 + terser-webpack-plugin: ^5.3.7 + watchpack: ^2.4.0 + webpack-sources: ^3.2.3 + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: b7d0e390f9d30627e303d54b17cb87b62f49ecffe2d35481f830679904993bae208e23748ffe0e6091b6dd4810562b2f2e88bb0f23b96515d74fb1e3c2898210 + languageName: node + linkType: hard + "websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": version: 0.7.4 resolution: "websocket-driver@npm:0.7.4" @@ -39319,7 +42474,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": +"yargs-parser@npm:^21.0.0, yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c @@ -39372,7 +42527,22 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1, yargs@npm:^17.7.1": +"yargs@npm:^17.3.1": + version: 17.5.1 + resolution: "yargs@npm:17.5.1" + dependencies: + cliui: ^7.0.2 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.3 + y18n: ^5.0.5 + yargs-parser: ^21.0.0 + checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde + languageName: node + linkType: hard + +"yargs@npm:^17.7.1": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 28b41fb07615be4c669ba773c3ab2f4f462b1a66 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Fri, 7 Jul 2023 15:55:25 -0300 Subject: [PATCH 097/149] fix: Canned Response custom tags break the GUI on enterprise (#29694) Co-authored-by: Kevin Aleman <kaleman960@gmail.com> --- .changeset/kind-coats-worry.md | 5 ++ .../client/components/Omnichannel/Tags.tsx | 51 ++++++++++--------- .../directory/chats/contextualBar/ChatInfo.js | 3 +- .../directory/chats/hooks/useTagsLabels.tsx | 21 ++++++++ .../server/lib/canned-responses.js | 13 +++-- .../ee/app/api-enterprise/server/lib/tags.ts | 33 ++++++++++++ .../server/hooks/afterTagRemoved.ts | 14 +++++ .../livechat-enterprise/server/hooks/index.ts | 1 + .../server/lib/LivechatEnterprise.ts | 1 + apps/meteor/ee/client/hooks/useTagsList.ts | 13 ++--- .../cannedResponses/CannedResponseEdit.tsx | 8 +-- .../modals/CreateCannedResponse/index.tsx | 3 +- .../ee/server/models/raw/CannedResponse.ts | 12 ++++- .../ee/server/models/raw/LivechatTag.ts | 8 ++- apps/meteor/lib/callbacks.ts | 2 + apps/meteor/tests/data/livechat/tags.ts | 9 ++-- .../api/livechat/15-canned-responses.ts | 41 ++++++++++++++- .../src/IOmnichannelCannedResponse.ts | 2 +- .../src/models/ICannedResponseModel.ts | 3 +- .../src/models/ILivechatTagModel.ts | 3 +- 20 files changed, 191 insertions(+), 55 deletions(-) create mode 100644 .changeset/kind-coats-worry.md create mode 100644 apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx create mode 100644 apps/meteor/ee/app/api-enterprise/server/lib/tags.ts create mode 100644 apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts diff --git a/.changeset/kind-coats-worry.md b/.changeset/kind-coats-worry.md new file mode 100644 index 000000000000..81140d95124f --- /dev/null +++ b/.changeset/kind-coats-worry.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed Canned Response custom tags breaking the GUI on enterprise diff --git a/apps/meteor/client/components/Omnichannel/Tags.tsx b/apps/meteor/client/components/Omnichannel/Tags.tsx index 3074639cee0d..19a8dba8d36f 100644 --- a/apps/meteor/client/components/Omnichannel/Tags.tsx +++ b/apps/meteor/client/components/Omnichannel/Tags.tsx @@ -2,25 +2,21 @@ import { Field, TextInput, Chip, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ChangeEvent, ReactElement } from 'react'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { useFormsSubscription } from '../../views/omnichannel/additionalForms'; import { FormSkeleton } from './Skeleton'; import { useLivechatTags } from './hooks/useLivechatTags'; -const Tags = ({ - tags = [], - handler, - error, - tagRequired, - department, -}: { +type TagsProps = { tags?: string[]; handler: (value: string[]) => void; error?: string; tagRequired?: boolean; department?: string; -}): ReactElement => { +}; + +const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps): ReactElement => { const t = useTranslation(); const forms = useFormsSubscription() as any; @@ -33,16 +29,20 @@ const Tags = ({ department, }); + const customTags = useMemo(() => { + return tags.filter((tag) => !tagsResult?.tags.find((rtag) => rtag._id === tag)); + }, [tags, tagsResult?.tags]); + const dispatchToastMessage = useToastMessageDispatch(); const [tagValue, handleTagValue] = useState(''); - const [paginatedTagValue, handlePaginatedTagValue] = useState<{ label: string; value: string }[]>(); + + const paginatedTagValue = useMemo(() => tags.map((tag) => ({ label: tag, value: tag })), [tags]); const removeTag = (tagToRemove: string): void => { - if (tags) { - const tagsFiltered = tags.filter((tag: string) => tag !== tagToRemove); - handler(tagsFiltered); - } + if (!tags) return; + + handler(tags.filter((tag) => tag !== tagToRemove)); }; const handleTagTextSubmit = useMutableCallback(() => { @@ -56,7 +56,7 @@ const Tags = ({ return; } - if (tags.includes(tagValue)) { + if (tags.some((tag) => tag === tagValue)) { dispatchToastMessage({ type: 'error', message: t('Tag_already_exists') }); return; } @@ -79,8 +79,7 @@ const Tags = ({ <EETagsComponent value={paginatedTagValue} handler={(tags: { label: string; value: string }[]): void => { - handler(tags.map((tag) => tag.label)); - handlePaginatedTagValue(tags); + handler(tags.map((tag) => tag.value)); }} department={department} /> @@ -99,16 +98,18 @@ const Tags = ({ {t('Add')} </Button> </Field.Row> - - <Field.Row justifyContent='flex-start'> - {tags?.map((tag, i) => ( - <Chip key={i} onClick={(): void => removeTag(tag)} mie='x8'> - {tag} - </Chip> - ))} - </Field.Row> </> )} + + {customTags.length > 0 && ( + <Field.Row justifyContent='flex-start'> + {customTags?.map((tag, i) => ( + <Chip key={i} onClick={(): void => removeTag(tag)} mie='x8'> + {tag} + </Chip> + ))} + </Field.Row> + )} </> ); }; diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js index 7c32188e7e34..386ef7655233 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js +++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js @@ -16,6 +16,7 @@ import Label from '../../../components/Label'; import { AgentField, SlaField, ContactField, SourceField } from '../../components'; import PriorityField from '../../components/PriorityField'; import { useOmnichannelRoomInfo } from '../../hooks/useOmnichannelRoomInfo'; +import { useTagsLabels } from '../hooks/useTagsLabels'; import DepartmentField from './DepartmentField'; import VisitorClientInfo from './VisitorClientInfo'; @@ -33,7 +34,6 @@ function ChatInfo({ id, route }) { const { ts, - tags, closedAt, departmentId, v, @@ -49,6 +49,7 @@ function ChatInfo({ id, route }) { queuedAt, } = room || { room: { v: {} } }; + const tags = useTagsLabels(room?.tags); const routePath = useRoute(route || 'omnichannel-directory'); const canViewCustomFields = usePermission('view-livechat-room-customfields'); const subscription = useUserSubscription(id); diff --git a/apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx b/apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx new file mode 100644 index 000000000000..f732a78ba3f5 --- /dev/null +++ b/apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx @@ -0,0 +1,21 @@ +import { useEndpoint } from '@rocket.chat/ui-contexts'; +import { useQuery } from '@tanstack/react-query'; +import { useMemo } from 'react'; + +// TODO: Remove this hook when the endpoint is changed +// to return labels instead of tag id's +export const useTagsLabels = (initialTags: string[] = []) => { + const getTags = useEndpoint('GET', '/v1/livechat/tags'); + const { data: tagsData, isInitialLoading } = useQuery(['/v1/livechat/tags'], () => getTags({ text: '' }), { + enabled: Boolean(initialTags.length), + }); + + const labels = useMemo(() => { + const { tags = [] } = tagsData || {}; + return tags.reduce<Record<string, string>>((acc, tag) => ({ ...acc, [tag._id]: tag.name }), {}); + }, [tagsData]); + + return useMemo(() => { + return isInitialLoading ? initialTags : initialTags.map((tag) => labels[tag] || tag); + }, [initialTags, isInitialLoading, labels]); +}; diff --git a/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js b/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js index 9f0872c38f29..1a7d478b6c3b 100644 --- a/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js +++ b/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js @@ -2,12 +2,13 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import { CannedResponse } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../../../app/authorization/server/functions/hasPermission'; +import { getTagsInformation } from './tags'; import { getDepartmentsWhichUserCanAccess } from '../../../livechat-enterprise/server/api/lib/departments'; export async function findAllCannedResponses({ userId }) { // If the user is an admin or livechat manager, get his own responses and all responses from all departments if (await hasPermissionAsync(userId, 'view-all-canned-responses')) { - return CannedResponse.find({ + const cannedResponses = await CannedResponse.find({ $or: [ { scope: 'user', @@ -21,20 +22,22 @@ export async function findAllCannedResponses({ userId }) { }, ], }).toArray(); + return getTagsInformation(cannedResponses); } // If the user it not any of the previous roles nor an agent, then get only his own responses if (!(await hasPermissionAsync(userId, 'view-agent-canned-responses'))) { - return CannedResponse.find({ + const cannedResponses = await CannedResponse.find({ scope: 'user', userId, }).toArray(); + return getTagsInformation(cannedResponses); } // Last scenario: user is an agent, so get his own responses and those from the departments he is in const accessibleDepartments = await getDepartmentsWhichUserCanAccess(userId); - return CannedResponse.find({ + const cannedResponses = await CannedResponse.find({ $or: [ { scope: 'user', @@ -51,6 +54,8 @@ export async function findAllCannedResponses({ userId }) { }, ], }).toArray(); + + return getTagsInformation(cannedResponses); } export async function findAllCannedResponsesFilter({ userId, shortcut, text, departmentId, scope, createdBy, tags = [], options = {} }) { @@ -124,7 +129,7 @@ export async function findAllCannedResponsesFilter({ userId, shortcut, text, dep }); const [cannedResponses, total] = await Promise.all([cursor.toArray(), totalCount]); return { - cannedResponses, + cannedResponses: await getTagsInformation(cannedResponses), total, }; } diff --git a/apps/meteor/ee/app/api-enterprise/server/lib/tags.ts b/apps/meteor/ee/app/api-enterprise/server/lib/tags.ts new file mode 100644 index 000000000000..c4ffc97da7f8 --- /dev/null +++ b/apps/meteor/ee/app/api-enterprise/server/lib/tags.ts @@ -0,0 +1,33 @@ +import type { ILivechatTag, IOmnichannelCannedResponse } from '@rocket.chat/core-typings'; +import { LivechatTag } from '@rocket.chat/models'; + +const filterTags = (tags: string[], serverTags: ILivechatTag[]) => { + return tags.reduce((acc, tag) => { + const found = serverTags.find((serverTag) => serverTag._id === tag); + if (found) { + acc.push(found.name); + } else { + acc.push(tag); + } + return acc; + }, [] as string[]); +}; + +export const getTagsInformation = async (cannedResponses: IOmnichannelCannedResponse[]) => { + return Promise.all( + cannedResponses.map(async (cannedResponse) => { + const { tags } = cannedResponse; + + if (!Array.isArray(tags) || !tags.length) { + return cannedResponse; + } + + const serverTags = await LivechatTag.findInIds(tags, { projection: { _id: 1, name: 1 } }).toArray(); + + // Known limitation: if a tag was added and removed before this, it will return the tag id instead of the name + cannedResponse.tags = filterTags(tags, serverTags); + + return cannedResponse; + }), + ); +}; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts new file mode 100644 index 000000000000..451ce972f62a --- /dev/null +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts @@ -0,0 +1,14 @@ +import { CannedResponse } from '@rocket.chat/models'; + +import { callbacks } from '../../../../../lib/callbacks'; + +callbacks.add( + 'livechat.afterTagRemoved', + async (tag) => { + const { _id } = tag; + + await CannedResponse.removeTagFromCannedResponses(_id); + }, + callbacks.priority.MEDIUM, + 'on-tag-removed-remove-references', +); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/index.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/index.ts index 3eba755b573d..c5bf0a5aa392 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/index.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/index.ts @@ -25,3 +25,4 @@ import './applySimultaneousChatsRestrictions'; import './afterInquiryQueued'; import './sendPdfTranscriptOnClose'; import './applyRoomRestrictions'; +import './afterTagRemoved'; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts index 653dfbda74e4..1471ef8d40e1 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts @@ -135,6 +135,7 @@ export const LivechatEnterprise = { throw new Meteor.Error('tag-not-found', 'Tag not found', { method: 'livechat:removeTag' }); } + await callbacks.run('livechat.afterTagRemoved', tag); return LivechatTag.removeById(_id); }, diff --git a/apps/meteor/ee/client/hooks/useTagsList.ts b/apps/meteor/ee/client/hooks/useTagsList.ts index 013a320fbade..229e495342e1 100644 --- a/apps/meteor/ee/client/hooks/useTagsList.ts +++ b/apps/meteor/ee/client/hooks/useTagsList.ts @@ -36,13 +36,14 @@ export const useTagsList = ( count: end + start, ...(options.department && { department: options.department }), }); + return { - items: tags.map((tag: any) => { - tag._updatedAt = new Date(tag._updatedAt); - tag.label = tag.name; - tag.value = { value: tag._id, label: tag.name }; - return tag; - }), + items: tags.map<any>((tag: any) => ({ + _id: tag._id, + label: tag.name, + value: tag._id, + _updatedAt: new Date(tag._updatedAt), + })), itemCount: total, }; }, diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx index daac8b053333..721b78d60dd0 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx @@ -39,10 +39,7 @@ const CannedResponseEdit: FC<{ _id: data?.cannedResponse ? data.cannedResponse._id : '', shortcut: data ? data.cannedResponse.shortcut : '', text: data ? data.cannedResponse.text : '', - tags: - data?.cannedResponse?.tags && Array.isArray(data.cannedResponse.tags) - ? data.cannedResponse.tags.map((tag) => ({ label: tag, value: tag })) - : [], + tags: data?.cannedResponse?.tags ?? [], scope: data ? data.cannedResponse.scope : 'user', departmentId: data?.cannedResponse?.departmentId ? data.cannedResponse.departmentId : '', }); @@ -100,13 +97,12 @@ const CannedResponseEdit: FC<{ tags: any; departmentId: string; }; - const mappedTags = tags.map((tag: string | { value: string; label: string }) => (typeof tag === 'object' ? tag?.value : tag)); await saveCannedResponse({ ...(_id && { _id }), shortcut, text, scope, - ...(mappedTags.length > 0 && { tags: mappedTags }), + tags, ...(departmentId && { departmentId }), }); dispatchToastMessage({ diff --git a/apps/meteor/ee/client/omnichannel/components/CannedResponse/modals/CreateCannedResponse/index.tsx b/apps/meteor/ee/client/omnichannel/components/CannedResponse/modals/CreateCannedResponse/index.tsx index fc19de948135..9285bc5ae942 100644 --- a/apps/meteor/ee/client/omnichannel/components/CannedResponse/modals/CreateCannedResponse/index.tsx +++ b/apps/meteor/ee/client/omnichannel/components/CannedResponse/modals/CreateCannedResponse/index.tsx @@ -77,13 +77,12 @@ const WrapCreateCannedResponseModal: FC<{ data?: any; reloadCannedList?: any }> tags: any; departmentId: string; }; - const mappedTags = tags.map((tag: string | { value: string; label: string }) => (typeof tag === 'object' ? tag?.value : tag)); await saveCannedResponse({ ...(_id && { _id }), shortcut, text, scope, - ...(tags.length > 0 && { tags: mappedTags }), + tags, ...(departmentId && { departmentId }), }); dispatchToastMessage({ diff --git a/apps/meteor/ee/server/models/raw/CannedResponse.ts b/apps/meteor/ee/server/models/raw/CannedResponse.ts index 5fd3f5c63974..e897db928dc2 100644 --- a/apps/meteor/ee/server/models/raw/CannedResponse.ts +++ b/apps/meteor/ee/server/models/raw/CannedResponse.ts @@ -1,6 +1,6 @@ import type { IOmnichannelCannedResponse } from '@rocket.chat/core-typings'; import type { ICannedResponseModel } from '@rocket.chat/model-typings'; -import type { Db, DeleteResult, FindCursor, FindOptions, IndexDescription } from 'mongodb'; +import type { Db, DeleteResult, FindCursor, FindOptions, IndexDescription, UpdateFilter } from 'mongodb'; import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; @@ -106,4 +106,14 @@ export class CannedResponseRaw extends BaseRaw<IOmnichannelCannedResponse> imple return this.deleteOne(query); } + + removeTagFromCannedResponses(tagId: string) { + const update: UpdateFilter<IOmnichannelCannedResponse> = { + $pull: { + tags: tagId, + }, + }; + + return this.updateMany({}, update); + } } diff --git a/apps/meteor/ee/server/models/raw/LivechatTag.ts b/apps/meteor/ee/server/models/raw/LivechatTag.ts index 465ed709b598..a4314c569f46 100644 --- a/apps/meteor/ee/server/models/raw/LivechatTag.ts +++ b/apps/meteor/ee/server/models/raw/LivechatTag.ts @@ -1,6 +1,6 @@ import type { ILivechatTag } from '@rocket.chat/core-typings'; import type { ILivechatTagModel } from '@rocket.chat/model-typings'; -import type { Db, DeleteResult, FindOptions, IndexDescription } from 'mongodb'; +import type { Db, DeleteResult, FindCursor, FindOptions, IndexDescription } from 'mongodb'; import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; @@ -26,6 +26,12 @@ export class LivechatTagRaw extends BaseRaw<ILivechatTag> implements ILivechatTa return this.findOne(query, options); } + findInIds(ids: string[], options?: FindOptions<ILivechatTag>): FindCursor<ILivechatTag> { + const query = { _id: { $in: ids } }; + + return this.find(query, options); + } + async createOrUpdateTag( _id: string | undefined, { name, description }: { name: string; description?: string }, diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 2fad995dfd1f..ce4c8517d2db 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -19,6 +19,7 @@ import type { ILivechatTag, SelectedAgent, InquiryWithAgentInfo, + ILivechatTagRecord, } from '@rocket.chat/core-typings'; import { Random } from '@rocket.chat/random'; @@ -95,6 +96,7 @@ interface EventLikeCallbackSignatures { 'livechat.afterDepartmentDisabled': (department: ILivechatDepartmentRecord) => void; 'livechat.afterDepartmentArchived': (department: Pick<ILivechatDepartmentRecord, '_id'>) => void; 'afterSaveUser': ({ user, oldUser }: { user: IUser; oldUser: IUser | null }) => void; + 'livechat.afterTagRemoved': (tag: ILivechatTagRecord) => void; } /** diff --git a/apps/meteor/tests/data/livechat/tags.ts b/apps/meteor/tests/data/livechat/tags.ts index 4ba5c78025cd..b12cca1f64ae 100644 --- a/apps/meteor/tests/data/livechat/tags.ts +++ b/apps/meteor/tests/data/livechat/tags.ts @@ -25,8 +25,8 @@ export const saveTags = (departments: string[] = []): Promise<ILivechatTag> => { }); }; -export const removeTag = (id: string): Promise<void> => { - return new Promise((resolve, reject) => { +export const removeTag = (id: string): Promise<boolean> => { + return new Promise((resolve, reject) => { request .post(methodCall(`livechat:removeTag`)) .set(credentials) @@ -43,6 +43,7 @@ export const removeTag = (id: string): Promise<void> => { return reject(err); } resolve(JSON.parse(res.body.message).result); - }); + } + ); }); -}; +}; \ No newline at end of file diff --git a/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts b/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts index fffe2f125d19..82b6eb6feb21 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts @@ -6,6 +6,7 @@ import { getCredentials, api, request, credentials } from '../../../data/api-dat import { updatePermission, updateSetting } from '../../../data/permissions.helper'; import { createCannedResponse } from '../../../data/livechat/canned-responses'; import { IS_EE } from '../../../e2e/config/constants'; +import { removeTag, saveTags } from '../../../data/livechat/tags'; (IS_EE ? describe : describe.skip)('[EE] LIVECHAT - Canned responses', function () { this.retries(0); @@ -72,11 +73,11 @@ import { IS_EE } from '../../../e2e/config/constants'; }); it('should return a canned response matching the params provided (tags)', async () => { const response = await createCannedResponse(); - const { body } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': response.tags[0] }).expect(200); + const { body } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': response.tags?.[0] }).expect(200); expect(body).to.have.property('success', true); expect(body.cannedResponses).to.be.an('array').with.lengthOf(1); expect(body.cannedResponses[0]).to.have.property('_id'); - expect(body.cannedResponses[0]).to.have.property('tags').that.is.an('array').which.includes(response.tags[0]); + expect(body.cannedResponses[0]).to.have.property('tags').that.is.an('array').which.includes(response.tags?.[0]); }); it('should return a canned response matching the params provided (text)', async () => { const response = await createCannedResponse(); @@ -129,6 +130,42 @@ import { IS_EE } from '../../../e2e/config/constants'; .send({ shortcut: 'shortcutxx', scope: 'user', tags: ['tag'], text: 'text' }) .expect(400); }); + it('should save a canned response related to an EE tag', async () => { + const tag = await saveTags(); + + const { body } = await request + .post(api('canned-responses')) + .set(credentials) + .send({ shortcut: 'shortcutxxx', scope: 'user', tags: [tag._id], text: 'text' }) + .expect(200); + + expect(body).to.have.property('success', true); + + const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag._id }).expect(200); + + expect(getResult).to.have.property('success', true); + expect(getResult.cannedResponses).to.be.an('array').with.lengthOf(1); + expect(getResult.cannedResponses[0]).to.have.property('_id'); + expect(getResult.cannedResponses[0]).to.have.property('tags').that.is.an('array').which.includes(tag.name); + }); + it('should not include removed tags in the response', async () => { + const tag = await saveTags(); + + const { body } = await request + .post(api('canned-responses')) + .set(credentials) + .send({ shortcut: 'shortcutxxxx', scope: 'user', tags: [tag._id], text: 'text' }) + .expect(200); + + expect(body).to.have.property('success', true); + + await removeTag(tag._id); + + const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag._id }).expect(200); + + expect(getResult).to.have.property('success', true); + expect(getResult.cannedResponses).to.be.an('array').with.lengthOf(0); + }); }); describe('[DELETE] canned-responses', () => { diff --git a/packages/core-typings/src/IOmnichannelCannedResponse.ts b/packages/core-typings/src/IOmnichannelCannedResponse.ts index 9887476c8b38..79e6b2b3a685 100644 --- a/packages/core-typings/src/IOmnichannelCannedResponse.ts +++ b/packages/core-typings/src/IOmnichannelCannedResponse.ts @@ -6,7 +6,7 @@ export interface IOmnichannelCannedResponse extends IRocketChatRecord { shortcut: string; text: string; scope: string; - tags: any; + tags: string[]; // userId is optional, its only required when scope === 'user' userId?: IUser['_id']; departmentId?: ILivechatDepartment['_id']; diff --git a/packages/model-typings/src/models/ICannedResponseModel.ts b/packages/model-typings/src/models/ICannedResponseModel.ts index bd8fd9fb0356..6cfa582c7825 100644 --- a/packages/model-typings/src/models/ICannedResponseModel.ts +++ b/packages/model-typings/src/models/ICannedResponseModel.ts @@ -1,5 +1,5 @@ import type { IOmnichannelCannedResponse } from '@rocket.chat/core-typings'; -import type { FindOptions, FindCursor, DeleteResult } from 'mongodb'; +import type { FindOptions, FindCursor, DeleteResult, UpdateResult, Document } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; @@ -24,4 +24,5 @@ export interface ICannedResponseModel extends IBaseModel<IOmnichannelCannedRespo _id: string, { shortcut, text, tags, scope, userId, departmentId, createdBy }: Omit<IOmnichannelCannedResponse, '_id' | '_updatedAt' | '_createdAt'>, ): Promise<Omit<IOmnichannelCannedResponse, '_updatedAt' | '_createdAt'>>; + removeTagFromCannedResponses(tagId: string): Promise<UpdateResult | Document>; } diff --git a/packages/model-typings/src/models/ILivechatTagModel.ts b/packages/model-typings/src/models/ILivechatTagModel.ts index 22aa655dbb9e..270e9ba99e09 100644 --- a/packages/model-typings/src/models/ILivechatTagModel.ts +++ b/packages/model-typings/src/models/ILivechatTagModel.ts @@ -1,10 +1,11 @@ import type { ILivechatTag } from '@rocket.chat/core-typings'; -import type { FindOptions, DeleteResult } from 'mongodb'; +import type { FindOptions, DeleteResult, FindCursor } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; export interface ILivechatTagModel extends IBaseModel<ILivechatTag> { findOneById(_id: string, options?: FindOptions<ILivechatTag>): Promise<ILivechatTag | null>; + findInIds(ids: string[], options?: FindOptions<ILivechatTag>): FindCursor<ILivechatTag>; createOrUpdateTag( _id: string | undefined, { name, description }: { name: string; description?: string }, From 1aebd7cab8640cece57a09ce5e04fca7f5c45555 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista <tasso.evangelista@rocket.chat> Date: Fri, 7 Jul 2023 18:57:26 -0300 Subject: [PATCH 098/149] refactor: Lazy initialization on client-side (#29764) --- apps/meteor/app/emoji-emojione/client/lib.ts | 8 +- .../app/emoji-emojione/lib/emojioneRender.ts | 9 - .../app/emoji-emojione/lib/getEmojiConfig.ts | 281 ++++++++++++++++++ .../app/emoji-emojione/lib/isSetNotNull.ts | 10 + .../app/emoji-emojione/lib/rocketchat.js | 280 ----------------- apps/meteor/app/emoji-emojione/server/lib.ts | 8 +- apps/meteor/app/emoji/client/lib.ts | 5 +- .../client/contexts/EmojiPickerContext.ts | 44 ++- .../client/lib/rooms/roomCoordinator.tsx | 2 +- .../client/providers/EmojiPickerProvider.tsx | 57 ++-- .../composer/EmojiPicker/EmojiPicker.tsx | 18 +- .../client/views/room/hooks/useOpenRoom.ts | 14 +- .../client/views/root/MainLayout/index.ts | 6 +- .../meteor/definition/externals/emojione.d.ts | 50 ++++ apps/meteor/package.json | 1 - yarn.lock | 1 - 16 files changed, 429 insertions(+), 365 deletions(-) delete mode 100644 apps/meteor/app/emoji-emojione/lib/emojioneRender.ts create mode 100644 apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts create mode 100644 apps/meteor/app/emoji-emojione/lib/isSetNotNull.ts delete mode 100644 apps/meteor/app/emoji-emojione/lib/rocketchat.js create mode 100644 apps/meteor/definition/externals/emojione.d.ts diff --git a/apps/meteor/app/emoji-emojione/client/lib.ts b/apps/meteor/app/emoji-emojione/client/lib.ts index 04671a6db6d8..408b053c20f8 100644 --- a/apps/meteor/app/emoji-emojione/client/lib.ts +++ b/apps/meteor/app/emoji-emojione/client/lib.ts @@ -1,8 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { emoji } from '../../emoji/client'; -import { getEmojiConfig, isSetNotNull } from '../lib/rocketchat'; import { getUserPreference } from '../../utils/client'; +import { isSetNotNull } from '../lib/isSetNotNull'; +import { getEmojiConfig } from '../lib/getEmojiConfig'; const config = getEmojiConfig(); @@ -18,16 +19,11 @@ if (emoji.packages.emojione) { // RocketChat.emoji.list is the collection of emojis from all emoji packages for (const [key, currentEmoji] of Object.entries(config.emojione.emojioneList)) { - // @ts-expect-error - emojione types currentEmoji.emojiPackage = 'emojione'; - // @ts-expect-error - emojione types emoji.list[key] = currentEmoji; - // @ts-expect-error - emojione types if (currentEmoji.shortnames) { - // @ts-expect-error - emojione types currentEmoji.shortnames.forEach((shortname: string) => { - // @ts-expect-error - emojione types emoji.list[shortname] = currentEmoji; }); } diff --git a/apps/meteor/app/emoji-emojione/lib/emojioneRender.ts b/apps/meteor/app/emoji-emojione/lib/emojioneRender.ts deleted file mode 100644 index 2ed772ce644c..000000000000 --- a/apps/meteor/app/emoji-emojione/lib/emojioneRender.ts +++ /dev/null @@ -1,9 +0,0 @@ -import emojione from 'emojione'; - -export function emojioneRender(message: string): string { - return emojione.toImage(message); -} - -export function emojioneRenderFromShort(message: string): string { - return emojione.shortnameToImage(message); -} diff --git a/apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts b/apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts new file mode 100644 index 000000000000..51d2fbc1c4ef --- /dev/null +++ b/apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts @@ -0,0 +1,281 @@ +import emojione from 'emojione'; +import mem from 'mem'; + +import { emojisByCategory, emojiCategories, toneList } from './emojiPicker'; + +// TODO remove fix below when issue is solved: https://github.com/joypixels/emojione/issues/617 + +// add missing emojis not provided by JS object, but included on emoji.json +emojione.shortnames += + '|:tm:|:copyright:|:registered:|:digit_zero:|:digit_one:|:digit_two:|:digit_three:|:digit_four:|:digit_five:|:digit_six:|:digit_seven:|:digit_eight:|:digit_nine:|:pound_symbol:|:asterisk_symbol:'; +emojione.regShortNames = new RegExp( + `<object[^>]*>.*?<\/object>|<span[^>]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|(${emojione.shortnames})`, + 'gi', +); + +emojione.emojioneList[':tm:'] = { + uc_base: '2122', + uc_output: '2122-fe0f', + uc_match: '2122-fe0f', + uc_greedy: '2122-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':copyright:'] = { + uc_base: '00a9', + uc_output: '00a9-f0ef', + uc_match: '00a9-fe0f', + uc_greedy: '00a9-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':registered:'] = { + uc_base: '00ae', + uc_output: '00ae-fe0f', + uc_match: '00ae-fe0f', + uc_greedy: '00ae-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_zero:'] = { + uc_base: '0030', + uc_output: '0030-fe0f', + uc_match: '0030-fe0f', + uc_greedy: '0030-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_one:'] = { + uc_base: '0031', + uc_output: '0031-fe0f', + uc_match: '0031-fe0f', + uc_greedy: '0031-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_two:'] = { + uc_base: '0032', + uc_output: '0032-fe0f', + uc_match: '0032-fe0f', + uc_greedy: '0032-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_three:'] = { + uc_base: '0033', + uc_output: '0033-fe0f', + uc_match: '0033-fe0f', + uc_greedy: '0033-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_four:'] = { + uc_base: '0034', + uc_output: '0034-fe0f', + uc_match: '0034-fe0f', + uc_greedy: '0034-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_five:'] = { + uc_base: '0035', + uc_output: '0035-fe0f', + uc_match: '0035-fe0f', + uc_greedy: '0035-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_six:'] = { + uc_base: '0036', + uc_output: '0036-fe0f', + uc_match: '0036-fe0f', + uc_greedy: '0036-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_seven:'] = { + uc_base: '0037', + uc_output: '0037-fe0f', + uc_match: '0037-fe0f', + uc_greedy: '0037-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_eight:'] = { + uc_base: '0038', + uc_output: '0038-fe0f', + uc_match: '0038-fe0f', + uc_greedy: '0038-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':digit_nine:'] = { + uc_base: '0039', + uc_output: '0039-fe0f', + uc_match: '0039-fe0f', + uc_greedy: '0039-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':pound_symbol:'] = { + uc_base: '0023', + uc_output: '0023-fe0f', + uc_match: '0023-fe0f', + uc_greedy: '0023-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; + +emojione.emojioneList[':asterisk_symbol:'] = { + uc_base: '002a', + uc_output: '002a-fe0f', + uc_match: '002a-fe0f', + uc_greedy: '002a-fe0f', + shortnames: [], + category: 'symbols', + emojiPackage: 'emojione', +}; +// end fix + +// fix for :+1: - had to replace all function that does its conversion: https://github.com/joypixels/emojione/blob/4.5.0/lib/js/emojione.js#L249 + +emojione.shortnameConversionMap = mem(emojione.shortnameConversionMap, { maxAge: 1000 }); + +emojione.unicodeCharRegex = mem(emojione.unicodeCharRegex, { maxAge: 1000 }); + +const convertShortName = mem( + (shortname) => { + // the fix is basically adding this .replace(/[+]/g, '\\$&') + if (typeof shortname === 'undefined' || shortname === '' || emojione.shortnames.indexOf(shortname.replace(/[+]/g, '\\$&')) === -1) { + // if the shortname doesnt exist just return the entire match + return shortname; + } + + // map shortname to parent + if (!emojione.emojioneList[shortname]) { + for (const emoji in emojione.emojioneList) { + if (!emojione.emojioneList.hasOwnProperty(emoji) || emoji === '') { + continue; + } + if (emojione.emojioneList[emoji].shortnames.indexOf(shortname) === -1) { + continue; + } + shortname = emoji; + break; + } + } + + const unicode = emojione.emojioneList[shortname].uc_output; + const fname = emojione.emojioneList[shortname].uc_base; + const category = fname.indexOf('-1f3f') >= 0 ? 'diversity' : emojione.emojioneList[shortname].category; + const title = emojione.imageTitleTag ? `title="${shortname}"` : ''; + // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; + // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path + const ePath = + emojione.defaultPathPNG !== emojione.imagePathPNG ? emojione.imagePathPNG : `${emojione.defaultPathPNG + emojione.emojiSize}/`; + + // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname + const alt = emojione.unicodeAlt ? emojione.convert(unicode.toUpperCase()) : shortname; + + if (emojione.sprites) { + return `<span class="emojione emojione-${category} _${fname}" ${title}>${alt}</span>`; + } + return `<img class="emojione" alt="${alt}" ${title} src="${ePath}${fname}${emojione.fileExtension}"/>`; + }, + { maxAge: 1000 }, +); + +const convertUnicode = mem( + (entire, _m1, m2, m3) => { + const mappedUnicode = emojione.mapUnicodeToShort(); + + if (typeof m3 === 'undefined' || m3 === '' || !(emojione.unescapeHTML(m3) in emojione.asciiList)) { + // if the ascii doesnt exist just return the entire match + return entire; + } + + m3 = emojione.unescapeHTML(m3); + const unicode = emojione.asciiList[m3]; + const shortname = mappedUnicode[unicode]; + const category = unicode.indexOf('-1f3f') >= 0 ? 'diversity' : emojione.emojioneList[shortname].category; + const title = emojione.imageTitleTag ? `title="${emojione.escapeHTML(m3)}"` : ''; + // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; + // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path + const ePath = + emojione.defaultPathPNG !== emojione.imagePathPNG ? emojione.imagePathPNG : `${emojione.defaultPathPNG + emojione.emojiSize}/`; + + // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname + const alt = emojione.unicodeAlt ? emojione.convert(unicode.toUpperCase()) : emojione.escapeHTML(m3); + + if (emojione.sprites) { + return `${m2}<span class="emojione emojione-${category} _${unicode}" ${title}>${alt}</span>`; + } + return `${m2}<img class="emojione" alt="${alt}" ${title} src="${ePath}${unicode}${emojione.fileExtension}"/>`; + }, + { maxAge: 1000, cacheKey: JSON.stringify }, +); + +emojione.shortnameToImage = (str) => { + // replace regular shortnames first + str = str.replace(emojione.regShortNames, convertShortName); + + // if ascii smileys are turned on, then we'll replace them! + if (emojione.ascii) { + const asciiRX = emojione.riskyMatchAscii ? emojione.regAsciiRisky : emojione.regAscii; + + return str.replace(asciiRX, convertUnicode); + } + + return str; +}; + +const isEmojiSupported = (str: string) => { + str = str.replace(emojione.regShortNames, convertShortName); + + // if ascii smileys are turned on, then we'll replace them! + if (emojione.ascii) { + const asciiRX = emojione.riskyMatchAscii ? emojione.regAsciiRisky : emojione.regAscii; + + return str.replace(asciiRX, convertUnicode); + } + + return str; +}; + +export const getEmojiConfig = () => ({ + emojione, + emojisByCategory, + emojiCategories, + toneList, + render: emojione.toImage, + renderPicker: emojione.shortnameToImage, + sprites: true, + isEmojiSupported, +}); diff --git a/apps/meteor/app/emoji-emojione/lib/isSetNotNull.ts b/apps/meteor/app/emoji-emojione/lib/isSetNotNull.ts new file mode 100644 index 000000000000..000be1279e68 --- /dev/null +++ b/apps/meteor/app/emoji-emojione/lib/isSetNotNull.ts @@ -0,0 +1,10 @@ +// http://stackoverflow.com/a/26990347 function isSet() from Gajus +export const isSetNotNull = async (fn: () => unknown) => { + let value; + try { + value = await fn(); + } catch (e) { + value = null; + } + return value !== null && value !== undefined; +}; diff --git a/apps/meteor/app/emoji-emojione/lib/rocketchat.js b/apps/meteor/app/emoji-emojione/lib/rocketchat.js deleted file mode 100644 index 7e415b99efe8..000000000000 --- a/apps/meteor/app/emoji-emojione/lib/rocketchat.js +++ /dev/null @@ -1,280 +0,0 @@ -import emojione from 'emojione'; -import mem from 'mem'; - -import { emojioneRender, emojioneRenderFromShort } from './emojioneRender'; -import { emojisByCategory, emojiCategories, toneList } from './emojiPicker'; - -// TODO remove fix below when issue is solved: https://github.com/joypixels/emojione/issues/617 - -// add missing emojis not provided by JS object, but included on emoji.json -emojione.shortnames += - '|:tm:|:copyright:|:registered:|:digit_zero:|:digit_one:|:digit_two:|:digit_three:|:digit_four:|:digit_five:|:digit_six:|:digit_seven:|:digit_eight:|:digit_nine:|:pound_symbol:|:asterisk_symbol:'; -emojione.regShortNames = new RegExp( - `<object[^>]*>.*?<\/object>|<span[^>]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|(${emojione.shortnames})`, - 'gi', -); - -emojione.emojioneList[':tm:'] = { - uc_base: '2122', - uc_output: '2122-fe0f', - uc_match: '2122-fe0f', - uc_greedy: '2122-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':copyright:'] = { - uc_base: '00a9', - uc_output: '00a9-f0ef', - uc_match: '00a9-fe0f', - uc_greedy: '00a9-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':registered:'] = { - uc_base: '00ae', - uc_output: '00ae-fe0f', - uc_match: '00ae-fe0f', - uc_greedy: '00ae-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_zero:'] = { - uc_base: '0030', - uc_output: '0030-fe0f', - uc_match: '0030-fe0f', - uc_greedy: '0030-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_one:'] = { - uc_base: '0031', - uc_output: '0031-fe0f', - uc_match: '0031-fe0f', - uc_greedy: '0031-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_two:'] = { - uc_base: '0032', - uc_output: '0032-fe0f', - uc_match: '0032-fe0f', - uc_greedy: '0032-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_three:'] = { - uc_base: '0033', - uc_output: '0033-fe0f', - uc_match: '0033-fe0f', - uc_greedy: '0033-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_four:'] = { - uc_base: '0034', - uc_output: '0034-fe0f', - uc_match: '0034-fe0f', - uc_greedy: '0034-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_five:'] = { - uc_base: '0035', - uc_output: '0035-fe0f', - uc_match: '0035-fe0f', - uc_greedy: '0035-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_six:'] = { - uc_base: '0036', - uc_output: '0036-fe0f', - uc_match: '0036-fe0f', - uc_greedy: '0036-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_seven:'] = { - uc_base: '0037', - uc_output: '0037-fe0f', - uc_match: '0037-fe0f', - uc_greedy: '0037-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_eight:'] = { - uc_base: '0038', - uc_output: '0038-fe0f', - uc_match: '0038-fe0f', - uc_greedy: '0038-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':digit_nine:'] = { - uc_base: '0039', - uc_output: '0039-fe0f', - uc_match: '0039-fe0f', - uc_greedy: '0039-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':pound_symbol:'] = { - uc_base: '0023', - uc_output: '0023-fe0f', - uc_match: '0023-fe0f', - uc_greedy: '0023-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; - -emojione.emojioneList[':asterisk_symbol:'] = { - uc_base: '002a', - uc_output: '002a-fe0f', - uc_match: '002a-fe0f', - uc_greedy: '002a-fe0f', - shortnames: [], - category: 'symbols', - emojiPackage: 'emojione', -}; -// end fix - -// fix for :+1: - had to replace all function that does its conversion: https://github.com/joypixels/emojione/blob/4.5.0/lib/js/emojione.js#L249 -(function (ns) { - ns.shortnameConversionMap = mem(ns.shortnameConversionMap, { maxAge: 1000 }); - - ns.unicodeCharRegex = mem(ns.unicodeCharRegex, { maxAge: 1000 }); - - const convertShortName = mem( - function (shortname) { - // the fix is basically adding this .replace(/[+]/g, '\\$&') - if (typeof shortname === 'undefined' || shortname === '' || ns.shortnames.indexOf(shortname.replace(/[+]/g, '\\$&')) === -1) { - // if the shortname doesnt exist just return the entire match - return shortname; - } - - // map shortname to parent - if (!ns.emojioneList[shortname]) { - for (const emoji in ns.emojioneList) { - if (!ns.emojioneList.hasOwnProperty(emoji) || emoji === '') { - continue; - } - if (ns.emojioneList[emoji].shortnames.indexOf(shortname) === -1) { - continue; - } - shortname = emoji; - break; - } - } - - const unicode = ns.emojioneList[shortname].uc_output; - const fname = ns.emojioneList[shortname].uc_base; - const category = fname.indexOf('-1f3f') >= 0 ? 'diversity' : ns.emojioneList[shortname].category; - const title = ns.imageTitleTag ? `title="${shortname}"` : ''; - // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; - // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path - const ePath = ns.defaultPathPNG !== ns.imagePathPNG ? ns.imagePathPNG : `${ns.defaultPathPNG + ns.emojiSize}/`; - - // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname - const alt = ns.unicodeAlt ? ns.convert(unicode.toUpperCase()) : shortname; - - if (ns.sprites) { - return `<span class="emojione emojione-${category} _${fname}" ${title}>${alt}</span>`; - } - return `<img class="emojione" alt="${alt}" ${title} src="${ePath}${fname}${ns.fileExtension}"/>`; - }, - { maxAge: 1000 }, - ); - - const convertUnicode = mem( - function (entire, m1, m2, m3) { - const mappedUnicode = ns.mapUnicodeToShort(); - - if (typeof m3 === 'undefined' || m3 === '' || !(ns.unescapeHTML(m3) in ns.asciiList)) { - // if the ascii doesnt exist just return the entire match - return entire; - } - - m3 = ns.unescapeHTML(m3); - const unicode = ns.asciiList[m3]; - const shortname = mappedUnicode[unicode]; - const category = unicode.indexOf('-1f3f') >= 0 ? 'diversity' : ns.emojioneList[shortname].category; - const title = ns.imageTitleTag ? `title="${ns.escapeHTML(m3)}"` : ''; - // const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32'; - // if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path - const ePath = ns.defaultPathPNG !== ns.imagePathPNG ? ns.imagePathPNG : `${ns.defaultPathPNG + ns.emojiSize}/`; - - // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname - const alt = ns.unicodeAlt ? ns.convert(unicode.toUpperCase()) : ns.escapeHTML(m3); - - if (ns.sprites) { - return `${m2}<span class="emojione emojione-${category} _${unicode}" ${title}>${alt}</span>`; - } - return `${m2}<img class="emojione" alt="${alt}" ${title} src="${ePath}${unicode}${ns.fileExtension}"/>`; - }, - { maxAge: 1000, cacheKey: JSON.stringify }, - ); - - ns.shortnameToImage = function (str) { - // replace regular shortnames first - str = str.replace(ns.regShortNames, convertShortName); - - // if ascii smileys are turned on, then we'll replace them! - if (ns.ascii) { - const asciiRX = ns.riskyMatchAscii ? ns.regAsciiRisky : ns.regAscii; - - return str.replace(asciiRX, convertUnicode); - } - - return str; - }; -})(emojione); - -export function getEmojiConfig() { - return { - emojione, - emojisByCategory, - emojiCategories, - toneList, - render: emojioneRender, - renderPicker: emojioneRenderFromShort, - sprites: true, - }; -} - -// http://stackoverflow.com/a/26990347 function isSet() from Gajus -export async function isSetNotNull(fn) { - let value; - try { - value = await fn(); - } catch (e) { - value = null; - } - return value !== null && value !== undefined; -} diff --git a/apps/meteor/app/emoji-emojione/server/lib.ts b/apps/meteor/app/emoji-emojione/server/lib.ts index 4f1808eba60a..b80b9318a828 100644 --- a/apps/meteor/app/emoji-emojione/server/lib.ts +++ b/apps/meteor/app/emoji-emojione/server/lib.ts @@ -1,7 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { emoji } from '../../emoji/server'; -import { getEmojiConfig, isSetNotNull } from '../lib/rocketchat'; +import { getEmojiConfig } from '../lib/getEmojiConfig'; +import { isSetNotNull } from '../lib/isSetNotNull'; import { getUserPreference } from '../../utils/server'; const config = getEmojiConfig(); @@ -20,16 +21,11 @@ if (emoji.packages.emojione) { for (const key in config.emojione.emojioneList) { if (config.emojione.emojioneList.hasOwnProperty(key)) { const currentEmoji = config.emojione.emojioneList[key]; - // @ts-expect-error - emojione types currentEmoji.emojiPackage = 'emojione'; - // @ts-expect-error - emojione types emoji.list[key] = currentEmoji; - // @ts-expect-error - emojione types if (currentEmoji.shortnames) { - // @ts-expect-error - emojione types currentEmoji.shortnames.forEach((shortname: string) => { - // @ts-expect-error - emojione types emoji.list[shortname] = currentEmoji; }); } diff --git a/apps/meteor/app/emoji/client/lib.ts b/apps/meteor/app/emoji/client/lib.ts index 55b858cb3e11..1d2397c9568d 100644 --- a/apps/meteor/app/emoji/client/lib.ts +++ b/apps/meteor/app/emoji/client/lib.ts @@ -1,4 +1,5 @@ -import { emojioneRender } from '../../emoji-emojione/lib/emojioneRender'; +import emojione from 'emojione'; + import type { EmojiPackages } from '../lib/rocketchat'; export const emoji: EmojiPackages = { @@ -10,7 +11,7 @@ export const emoji: EmojiPackages = { recent: [], }, toneList: {}, - render: emojioneRender, + render: emojione.toImage, renderPicker(emojiToRender) { const correctPackage = emoji.list[emojiToRender].emojiPackage; if (!correctPackage) { diff --git a/apps/meteor/client/contexts/EmojiPickerContext.ts b/apps/meteor/client/contexts/EmojiPickerContext.ts index 50490182d519..4c4257c3dd3d 100644 --- a/apps/meteor/client/contexts/EmojiPickerContext.ts +++ b/apps/meteor/client/contexts/EmojiPickerContext.ts @@ -10,7 +10,7 @@ type EmojiPickerContextValue = { handlePreview: (emoji: string, name: string) => void; handleRemovePreview: () => void; addRecentEmoji: (emoji: string) => void; - emojiListByCategory: EmojiByCategory[]; + getEmojiListsByCategory: () => EmojiByCategory[]; recentEmojis: string[]; setRecentEmojis: (emoji: string[]) => void; actualTone: number; @@ -44,16 +44,32 @@ export const usePreviewEmoji = () => ({ handleRemovePreview: useEmojiPickerContext().handleRemovePreview, }); -export const useEmojiPickerData = () => ({ - addRecentEmoji: useEmojiPickerContext().addRecentEmoji, - emojiListByCategory: useEmojiPickerContext().emojiListByCategory, - recentEmojis: useEmojiPickerContext().recentEmojis, - setRecentEmojis: useEmojiPickerContext().setRecentEmojis, - actualTone: useEmojiPickerContext().actualTone, - currentCategory: useEmojiPickerContext().currentCategory, - setCurrentCategory: useEmojiPickerContext().setCurrentCategory, - customItemsLimit: useEmojiPickerContext().customItemsLimit, - setCustomItemsLimit: useEmojiPickerContext().setCustomItemsLimit, - setActualTone: useEmojiPickerContext().setActualTone, - quickReactions: useEmojiPickerContext().quickReactions, -}); +export const useEmojiPickerData = () => { + const { + actualTone, + addRecentEmoji, + currentCategory, + customItemsLimit, + getEmojiListsByCategory, + quickReactions, + recentEmojis, + setActualTone, + setCurrentCategory, + setCustomItemsLimit, + setRecentEmojis, + } = useEmojiPickerContext(); + + return { + addRecentEmoji, + getEmojiListsByCategory, + recentEmojis, + setRecentEmojis, + actualTone, + currentCategory, + setCurrentCategory, + customItemsLimit, + setCustomItemsLimit, + setActualTone, + quickReactions, + }; +}; diff --git a/apps/meteor/client/lib/rooms/roomCoordinator.tsx b/apps/meteor/client/lib/rooms/roomCoordinator.tsx index 9a2e3d14ab47..31eda2982a12 100644 --- a/apps/meteor/client/lib/rooms/roomCoordinator.tsx +++ b/apps/meteor/client/lib/rooms/roomCoordinator.tsx @@ -19,7 +19,7 @@ import type { import { RoomCoordinator } from '../../../lib/rooms/coordinator'; import { router } from '../../providers/RouterProvider'; import RoomRoute from '../../views/room/RoomRoute'; -import MainLayout from '../../views/root/MainLayout/MainLayout'; +import MainLayout from '../../views/root/MainLayout'; import { appLayout } from '../appLayout'; class RoomCoordinatorClient extends RoomCoordinator { diff --git a/apps/meteor/client/providers/EmojiPickerProvider.tsx b/apps/meteor/client/providers/EmojiPickerProvider.tsx index 5c9f3a8e9946..84ffa3394c10 100644 --- a/apps/meteor/client/providers/EmojiPickerProvider.tsx +++ b/apps/meteor/client/providers/EmojiPickerProvider.tsx @@ -1,5 +1,5 @@ import { useDebouncedState, useLocalStorage } from '@rocket.chat/fuselage-hooks'; -import type { ReactNode, ReactElement } from 'react'; +import type { ReactNode, ReactElement, ContextType } from 'react'; import React, { useState, useCallback, useMemo, useEffect } from 'react'; import type { EmojiByCategory } from '../../app/emoji/client'; @@ -14,7 +14,6 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen const [emojiToPreview, setEmojiToPreview] = useDebouncedState<{ emoji: string; name: string } | null>(null, 100); const [recentEmojis, setRecentEmojis] = useLocalStorage<string[]>('emoji.recent', []); const [actualTone, setActualTone] = useLocalStorage('emoji.tone', 0); - const [emojiListByCategory, setEmojiListByCategory] = useState<EmojiByCategory[]>([]); const [currentCategory, setCurrentCategory] = useState('recent'); const [customItemsLimit, setCustomItemsLimit] = useState(DEFAULT_ITEMS_LIMIT); @@ -40,26 +39,37 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen [frequentEmojis, setFrequentEmojis], ); + const [getEmojiListsByCategory, setEmojiListsByCategoryGetter] = useState<() => EmojiByCategory[]>(() => () => []); + // TODO: improve this update const updateEmojiListByCategory = useCallback( (categoryKey: string, limit: number = DEFAULT_ITEMS_LIMIT) => { - const result = emojiListByCategory.map((category) => { - return categoryKey === category.key - ? { - ...category, - emojis: { - list: createEmojiList(category.key, null, recentEmojis, setRecentEmojis), - limit: category.key === CUSTOM_CATEGORY ? limit | customItemsLimit : null, - }, - } - : category; - }); - - setEmojiListByCategory(result); + setEmojiListsByCategoryGetter( + (getEmojiListsByCategory) => () => + getEmojiListsByCategory().map((category) => + categoryKey === category.key + ? { + ...category, + emojis: { + list: createEmojiList(category.key, null, recentEmojis, setRecentEmojis), + limit: category.key === CUSTOM_CATEGORY ? limit | customItemsLimit : null, + }, + } + : category, + ), + ); }, - [customItemsLimit, emojiListByCategory, recentEmojis, setRecentEmojis], + [customItemsLimit, recentEmojis, setRecentEmojis], ); + useEffect(() => { + if (recentEmojis?.length > 0) { + updateRecent(recentEmojis); + } + + setEmojiListsByCategoryGetter(() => () => createPickerEmojis(customItemsLimit, actualTone, recentEmojis, setRecentEmojis)); + }, [actualTone, recentEmojis, customItemsLimit, currentCategory, setRecentEmojis, frequentEmojis]); + const addRecentEmoji = useCallback( (_emoji: string) => { addFrequentEmojis(_emoji); @@ -83,21 +93,12 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen [recentEmojis, setRecentEmojis, updateEmojiListByCategory, addFrequentEmojis], ); - useEffect(() => { - if (recentEmojis?.length > 0) { - updateRecent(recentEmojis); - } - - const emojis = createPickerEmojis(customItemsLimit, actualTone, recentEmojis, setRecentEmojis); - setEmojiListByCategory(emojis); - }, [actualTone, recentEmojis, customItemsLimit, currentCategory, setRecentEmojis, frequentEmojis]); - const open = useCallback((ref: Element, callback: (emoji: string) => void) => { return setEmojiPicker(<EmojiPicker reference={ref} onClose={() => setEmojiPicker(null)} onPickEmoji={(emoji) => callback(emoji)} />); }, []); const contextValue = useMemo( - () => ({ + (): ContextType<typeof EmojiPickerContext> => ({ isOpen: emojiPicker !== null, close: () => setEmojiPicker(null), open, @@ -105,7 +106,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen handlePreview: (emoji: string, name: string) => setEmojiToPreview({ emoji, name }), handleRemovePreview: () => setEmojiToPreview(null), addRecentEmoji, - emojiListByCategory, + getEmojiListsByCategory, recentEmojis, setRecentEmojis, actualTone, @@ -122,7 +123,7 @@ const EmojiPickerProvider = ({ children }: { children: ReactNode }): ReactElemen emojiToPreview, setEmojiToPreview, addRecentEmoji, - emojiListByCategory, + getEmojiListsByCategory, recentEmojis, setRecentEmojis, actualTone, diff --git a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx index cd37a05699fb..47d8ba849201 100644 --- a/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx +++ b/apps/meteor/client/views/composer/EmojiPicker/EmojiPicker.tsx @@ -11,7 +11,7 @@ import { } from '@rocket.chat/ui-client'; import { useTranslation, usePermission, useRoute } from '@rocket.chat/ui-contexts'; import type { ChangeEvent, KeyboardEvent, MouseEvent, RefObject } from 'react'; -import React, { useLayoutEffect, useState, useEffect, useRef, useCallback } from 'react'; +import React, { useLayoutEffect, useState, useEffect, useRef } from 'react'; import type { VirtuosoHandle } from 'react-virtuoso'; import type { EmojiItem, EmojiCategoryPosition } from '../../../../app/emoji/client'; @@ -59,13 +59,13 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { setRecentEmojis, actualTone, currentCategory, - emojiListByCategory, + getEmojiListsByCategory, customItemsLimit, setActualTone, setCustomItemsLimit, } = useEmojiPickerData(); - useEffect(() => () => handleRemovePreview(), []); + useEffect(() => () => handleRemovePreview(), [handleRemovePreview]); const scrollCategories = useMediaQuery('(width < 340px)'); @@ -126,16 +126,12 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { onClose(); }; - const showInitialCategory = useCallback((customEmojiList) => { - handleGoToCategory(customEmojiList.length > 0 ? 0 : 1); - }, []); - useEffect(() => { if (recentEmojis.length === 0 && currentCategory === 'recent') { - const customEmojiList = emojiListByCategory.filter(({ key }) => key === 'rocket'); - showInitialCategory(customEmojiList); + const customEmojiList = getEmojiListsByCategory().filter(({ key }) => key === 'rocket'); + handleGoToCategory(customEmojiList.length > 0 ? 0 : 1); } - }, [actualTone, recentEmojis, emojiListByCategory, currentCategory, setRecentEmojis, showInitialCategory]); + }, [actualTone, recentEmojis, getEmojiListsByCategory, currentCategory, setRecentEmojis]); const handleSearch = (e: ChangeEvent<HTMLInputElement>) => { setSearchTerm(e.target.value); @@ -216,7 +212,7 @@ const EmojiPicker = ({ reference, onClose, onPickEmoji }: EmojiPickerProps) => { {!searching && ( <CategoriesResult ref={virtuosoRef} - emojiListByCategory={emojiListByCategory} + emojiListByCategory={getEmojiListsByCategory()} categoriesPosition={categoriesPosition} customItemsLimit={customItemsLimit} handleLoadMore={handleLoadMore} diff --git a/apps/meteor/client/views/room/hooks/useOpenRoom.ts b/apps/meteor/client/views/room/hooks/useOpenRoom.ts index 4e109759593a..7fa0114bcd64 100644 --- a/apps/meteor/client/views/room/hooks/useOpenRoom.ts +++ b/apps/meteor/client/views/room/hooks/useOpenRoom.ts @@ -3,15 +3,10 @@ import { useMethod, useRoute, useSetting, useUser } from '@rocket.chat/ui-contex import { useQuery } from '@tanstack/react-query'; import { useRef } from 'react'; -import { ChatRoom, ChatSubscription } from '../../../../app/models/client'; -import { LegacyRoomManager } from '../../../../app/ui-utils/client'; import { roomFields } from '../../../../lib/publishFields'; import { omit } from '../../../../lib/utils/omit'; -import { RoomManager } from '../../../lib/RoomManager'; import { NotAuthorizedError } from '../../../lib/errors/NotAuthorizedError'; import { RoomNotFoundError } from '../../../lib/errors/RoomNotFoundError'; -import { fireGlobalEvent } from '../../../lib/utils/fireGlobalEvent'; -import { waitUntilFind } from '../../../lib/utils/waitUntilFind'; export function useOpenRoom({ type, reference }: { type: RoomType; reference: string }) { const user = useUser(); @@ -53,6 +48,8 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st } } + const { ChatRoom, ChatSubscription } = await import('../../../../app/models/client'); + ChatRoom.upsert({ _id: roomData._id }, { $set, $unset }); const room = ChatRoom.findOne({ _id: roomData._id }); @@ -60,12 +57,17 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st throw new TypeError('room is undefined'); } + const { LegacyRoomManager } = await import('../../../../app/ui-utils/client'); + if (room._id !== reference && type === 'd') { // Redirect old url using username to rid await LegacyRoomManager.close(type + reference); throw new RoomNotFoundError(undefined, { rid: room._id }); } + const { RoomManager } = await import('../../../lib/RoomManager'); + const { fireGlobalEvent } = await import('../../../lib/utils/fireGlobalEvent'); + unsubscribeFromRoomOpenedEvent.current(); unsubscribeFromRoomOpenedEvent.current = RoomManager.once('opened', () => fireGlobalEvent('room-opened', omit(room, 'usernames'))); @@ -95,6 +97,8 @@ export function useOpenRoom({ type, reference }: { type: RoomType; reference: st } const { rid } = await createDirectMessage(...reference.split(', ')); + const { ChatSubscription } = await import('../../../../app/models/client'); + const { waitUntilFind } = await import('../../../lib/utils/waitUntilFind'); await waitUntilFind(() => ChatSubscription.findOne({ rid })); directRoute.push({ rid }, (prev) => prev); }, diff --git a/apps/meteor/client/views/root/MainLayout/index.ts b/apps/meteor/client/views/root/MainLayout/index.ts index ed5671ffcf21..4b36da185ab3 100644 --- a/apps/meteor/client/views/root/MainLayout/index.ts +++ b/apps/meteor/client/views/root/MainLayout/index.ts @@ -1 +1,5 @@ -export { default } from './MainLayout'; +import { lazy } from 'react'; + +const MainLayout = lazy(() => import('./MainLayout')); + +export default MainLayout; diff --git a/apps/meteor/definition/externals/emojione.d.ts b/apps/meteor/definition/externals/emojione.d.ts new file mode 100644 index 000000000000..6e9c9deab0c7 --- /dev/null +++ b/apps/meteor/definition/externals/emojione.d.ts @@ -0,0 +1,50 @@ +declare module 'emojione' { + export as namespace emojione; + + export let sprites: boolean; + export let imagePathSVG: string; + export let imagePathSVGSprites: string; + export let imageType: 'png' | 'svg'; + export let unicodeAlt: boolean; + export let ascii: boolean; + export let unicodeRegexp: string; + export let cacheBustParam: string; + export let emojioneList: { + [key: string]: { + uc_base: string; + uc_output: string; + uc_match: string; + uc_greedy: string; + shortnames: string[]; + category: string; + emojiPackage: string; + }; + }; + + type Unicode = string; + type MappedUnicode = Record<Unicode, string>; + + export let regShortNames: RegExp; + export let shortnames: string; + export const imageTitleTag: boolean; + export const defaultPathPNG: string; + export let imagePathPNG: string; + export const emojiSize: string; + export const fileExtension: string; + export const asciiList: Record<string, Unicode>; + export const riskyMatchAscii: boolean; + export const regAsciiRisky: RegExp; + export const regAscii: RegExp; + + export function toShort(str: string): string; + export function toImage(str: string): string; + export function shortnameToImage(str: string): string; + export function unicodeToImage(str: string): string; + export function shortnameToUnicode(str: string): string; + export function shortnameConversionMap(): unknown; + export function unicodeCharRegex(): unknown; + export function convert(str: string): string; + export function mapUnicodeToShort(): MappedUnicode; + export function escapeHTML(str: string): string; + export function unescapeHTML(str: string): string; +} diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 0370b5327b4f..80289d401c38 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -104,7 +104,6 @@ "@types/cssom": "^0.4.1", "@types/dompurify": "^2.3.3", "@types/ejson": "^2.2.0", - "@types/emojione": "^2.2.6", "@types/express": "^4.17.17", "@types/express-rate-limit": "^5.1.3", "@types/fibers": "^3.1.1", diff --git a/yarn.lock b/yarn.lock index 3dcf48ca6520..49aef6230cd1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10299,7 +10299,6 @@ __metadata: "@types/cssom": ^0.4.1 "@types/dompurify": ^2.3.3 "@types/ejson": ^2.2.0 - "@types/emojione": ^2.2.6 "@types/express": ^4.17.17 "@types/express-rate-limit": ^5.1.3 "@types/fibers": ^3.1.1 From ad014d1e347104c48823895e7c93c1d350463deb Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Fri, 7 Jul 2023 20:37:02 -0300 Subject: [PATCH 099/149] chore: `CloudRoute` to typescript (#29762) --- apps/meteor/app/emoji/client/helpers.ts | 2 +- apps/meteor/client/lib/constants.ts | 1 + .../views/admin/cloud/{CloudRoute.js => CloudRoute.tsx} | 4 ++-- apps/meteor/client/views/admin/cloud/CopyStep.tsx | 4 ++-- apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx | 3 +-- .../views/admin/cloud/components/RegisterWorkspaceMenu.tsx | 4 ++-- apps/meteor/client/views/admin/cloud/constants.js | 1 - 7 files changed, 9 insertions(+), 10 deletions(-) rename apps/meteor/client/views/admin/cloud/{CloudRoute.js => CloudRoute.tsx} (92%) delete mode 100644 apps/meteor/client/views/admin/cloud/constants.js diff --git a/apps/meteor/app/emoji/client/helpers.ts b/apps/meteor/app/emoji/client/helpers.ts index d9fba8720d1c..35badda26a73 100644 --- a/apps/meteor/app/emoji/client/helpers.ts +++ b/apps/meteor/app/emoji/client/helpers.ts @@ -159,7 +159,7 @@ export const updateRecent = (recentList: string[]) => { const getEmojiRender = (emojiName: string) => { const emojiPackageName = emoji.list[emojiName]?.emojiPackage; const emojiPackage = emoji.packages[emojiPackageName]; - return emojiPackage.render(emojiName); + return emojiPackage?.render(emojiName); }; export const getFrequentEmoji = (frequentEmoji: string[]) => { diff --git a/apps/meteor/client/lib/constants.ts b/apps/meteor/client/lib/constants.ts index 89052eea93ac..7e437deb7fd2 100644 --- a/apps/meteor/client/lib/constants.ts +++ b/apps/meteor/client/lib/constants.ts @@ -1,3 +1,4 @@ export const USER_STATUS_TEXT_MAX_LENGTH = 120; export const BIO_TEXT_MAX_LENGTH = 260; export const VIDEOCONF_STACK_MAX_USERS = 6; +export const CLOUD_CONSOLE_URL = 'https://cloud.rocket.chat'; diff --git a/apps/meteor/client/views/admin/cloud/CloudRoute.js b/apps/meteor/client/views/admin/cloud/CloudRoute.tsx similarity index 92% rename from apps/meteor/client/views/admin/cloud/CloudRoute.js rename to apps/meteor/client/views/admin/cloud/CloudRoute.tsx index 705136294a3b..89bf5f1df772 100644 --- a/apps/meteor/client/views/admin/cloud/CloudRoute.js +++ b/apps/meteor/client/views/admin/cloud/CloudRoute.tsx @@ -4,7 +4,7 @@ import React from 'react'; import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; import RegisterWorkspace from './RegisterWorkspace'; -function CloudRoute() { +const CloudRoute = () => { const canManageCloud = usePermission('manage-cloud'); if (!canManageCloud) { @@ -12,6 +12,6 @@ function CloudRoute() { } return <RegisterWorkspace />; -} +}; export default CloudRoute; diff --git a/apps/meteor/client/views/admin/cloud/CopyStep.tsx b/apps/meteor/client/views/admin/cloud/CopyStep.tsx index 96b6e14eaac5..4232572ea7bf 100644 --- a/apps/meteor/client/views/admin/cloud/CopyStep.tsx +++ b/apps/meteor/client/views/admin/cloud/CopyStep.tsx @@ -5,7 +5,7 @@ import type { FC } from 'react'; import React, { useEffect, useState, useRef } from 'react'; import MarkdownText from '../../../components/MarkdownText'; -import { cloudConsoleUrl } from './constants'; +import { CLOUD_CONSOLE_URL } from '../../../lib/constants'; type CopyStepProps = { onNextButtonClick: () => void; @@ -61,7 +61,7 @@ const CopyStep: FC<CopyStepProps> = ({ onNextButtonClick }) => { <Icon name='copy' /> {t('Copy')} </Button> </Box> - <MarkdownText preserveHtml={true} content={t('Cloud_click_here', { cloudConsoleUrl })} /> + <MarkdownText preserveHtml={true} content={t('Cloud_click_here', { CLOUD_CONSOLE_URL })} /> </Modal.Content> <Modal.Footer> <Modal.FooterControllers> diff --git a/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx b/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx index 9e05ef111be5..9e4e479f16f9 100644 --- a/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx +++ b/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx @@ -2,7 +2,6 @@ import { Box, Tag } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; -import type { ReactNode } from 'react'; import React from 'react'; import Page from '../../../components/Page'; @@ -12,7 +11,7 @@ import RegisterWorkspaceMenu from './components/RegisterWorkspaceMenu'; import ConnectWorkspaceModal from './modals/ConnectWorkspaceModal'; import RegisterWorkspaceModal from './modals/RegisterWorkspaceModal'; -const RegisterWorkspace = (): ReactNode => { +const RegisterWorkspace = () => { const t = useTranslation(); const setModal = useSetModal(); diff --git a/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx b/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx index b24cd12bbab2..a3de1c5384c8 100644 --- a/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx +++ b/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx @@ -3,7 +3,7 @@ import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import { useExternalLink } from '../../../../hooks/useExternalLink'; -import { cloudConsoleUrl } from '../constants'; +import { CLOUD_CONSOLE_URL } from '../../../../lib/constants'; import RegisteredWorkspaceModal from '../modals/RegisteredWorkspaceModal'; type RegisterWorkspaceMenuProps = { @@ -35,7 +35,7 @@ const RegisterWorkspaceMenu = ({ <ButtonGroup> {isWorkspaceRegistered && isConnectedToCloud && ( <> - <Button role='link' onClick={() => handleLinkClick(cloudConsoleUrl)}> + <Button role='link' onClick={() => handleLinkClick(CLOUD_CONSOLE_URL)}> <Icon name='new-window' size='x20' pie={4} /> {t('Cloud')} </Button> diff --git a/apps/meteor/client/views/admin/cloud/constants.js b/apps/meteor/client/views/admin/cloud/constants.js deleted file mode 100644 index d9cf214b19db..000000000000 --- a/apps/meteor/client/views/admin/cloud/constants.js +++ /dev/null @@ -1 +0,0 @@ -export const cloudConsoleUrl = 'https://cloud.rocket.chat'; From 5ff71b3f82199eef39e8dd5ed12bcbc453186b6f Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat> Date: Sat, 8 Jul 2023 00:06:49 -0300 Subject: [PATCH 100/149] regression: Current Chats tags filter not showing department tags (#29758) --- .../omnichannel/currentChats/FilterByText.tsx | 2 +- .../chats/contextualBar/RoomEdit/RoomEdit.tsx | 2 +- apps/meteor/ee/client/hooks/useTagsList.ts | 17 ++++++++++------- .../tags/AutoCompleteTagsMultiple.js | 7 +++++-- .../client/omnichannel/tags/CurrentChatTags.tsx | 6 ++++-- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx b/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx index 6587563caa5f..3659f77b394f 100644 --- a/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx +++ b/apps/meteor/client/views/omnichannel/currentChats/FilterByText.tsx @@ -150,7 +150,7 @@ const FilterByText: FilterByTextType = ({ setFilter, reload, customFields, setCu <Box display='flex' flexDirection='row' marginBlockStart='x8' {...props}> <Box display='flex' mie='x8' flexGrow={1} flexDirection='column'> <Label mb='x4'>{t('Tags')}</Label> - <EETagsComponent value={tags} handler={handleTags} /> + <EETagsComponent value={tags} handler={handleTags} viewAll /> </Box> </Box> )} diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEdit.tsx b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEdit.tsx index 435e29743469..433af2135068 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEdit.tsx +++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEdit.tsx @@ -134,7 +134,7 @@ function RoomEdit({ room, visitor, reload, reloadInfo, onClose }: RoomEditProps) </Field> <Field> - <Tags tags={tagsField.value} handler={tagsField.onChange} /> + <Tags tags={tagsField.value} handler={tagsField.onChange} department={room.departmentId} /> </Field> {SlaPoliciesSelect && !!slaPolicies?.length && ( diff --git a/apps/meteor/ee/client/hooks/useTagsList.ts b/apps/meteor/ee/client/hooks/useTagsList.ts index 229e495342e1..78dfc4de0093 100644 --- a/apps/meteor/ee/client/hooks/useTagsList.ts +++ b/apps/meteor/ee/client/hooks/useTagsList.ts @@ -9,16 +9,18 @@ import { RecordList } from '../../../client/lib/lists/RecordList'; type TagsListOptions = { filter: string; department?: string; + viewAll?: boolean; }; -export const useTagsList = ( - options: TagsListOptions, -): { +type UseTagsListResult = { itemsList: RecordList<ILivechatTagRecord>; initialItemCount: number; reload: () => void; loadMoreItems: (start: number, end: number) => void; -} => { +}; + +export const useTagsList = (options: TagsListOptions): UseTagsListResult => { + const { viewAll, department, filter } = options; const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatTagRecord>()); const reload = useCallback(() => setItemsList(new RecordList<ILivechatTagRecord>()), []); @@ -31,10 +33,11 @@ export const useTagsList = ( const fetchData = useCallback( async (start, end) => { const { tags, total } = await getTags({ - text: options.filter, + text: filter, offset: start, count: end + start, - ...(options.department && { department: options.department }), + ...(viewAll && { viewAll: 'true' }), + ...(department && { department }), }); return { @@ -47,7 +50,7 @@ export const useTagsList = ( itemCount: total, }; }, - [getTags, options.filter, options.department], + [getTags, filter, viewAll, department], ); const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25); diff --git a/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js b/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js index 5f7fe4a4ffe6..086d796152fc 100644 --- a/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js +++ b/apps/meteor/ee/client/omnichannel/tags/AutoCompleteTagsMultiple.js @@ -8,7 +8,7 @@ import { AsyncStatePhase } from '../../../../client/hooks/useAsyncState'; import { useTagsList } from '../../hooks/useTagsList'; const AutoCompleteTagMultiple = (props) => { - const { value, onlyMyTags = false, onChange = () => {}, department } = props; + const { value, onlyMyTags = false, onChange = () => {}, department, viewAll = false } = props; const t = useTranslation(); const [tagsFilter, setTagsFilter] = useState(''); @@ -16,7 +16,10 @@ const AutoCompleteTagMultiple = (props) => { const debouncedTagsFilter = useDebouncedValue(tagsFilter, 500); const { itemsList: tagsList, loadMoreItems: loadMoreTags } = useTagsList( - useMemo(() => ({ filter: debouncedTagsFilter, onlyMyTags, department }), [debouncedTagsFilter, onlyMyTags, department]), + useMemo( + () => ({ filter: debouncedTagsFilter, onlyMyTags, department, viewAll }), + [debouncedTagsFilter, onlyMyTags, department, viewAll], + ), ); const { phase: tagsPhase, items: tagsItems, itemCount: tagsTotal } = useRecordList(tagsList); diff --git a/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx b/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx index 7253764c52b6..61c1d11af947 100644 --- a/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx +++ b/apps/meteor/ee/client/omnichannel/tags/CurrentChatTags.tsx @@ -3,8 +3,10 @@ import React from 'react'; import AutoCompleteTagsMultiple from './AutoCompleteTagsMultiple'; -const CurrentChatTags: FC<{ value: Array<string>; handler: () => void; department?: string }> = ({ value, handler, department }) => ( - <AutoCompleteTagsMultiple onChange={handler} value={value} department={department} /> +type CurrentChatTagsProps = { value: Array<string>; handler: () => void; department?: string; viewAll?: boolean }; + +const CurrentChatTags: FC<CurrentChatTagsProps> = ({ value, handler, department, viewAll }) => ( + <AutoCompleteTagsMultiple onChange={handler} value={value} department={department} viewAll={viewAll} /> ); export default CurrentChatTags; From 9f18c611846fbdf7e6b8d5ab72d0b2a38c8612bd Mon Sep 17 00:00:00 2001 From: Ujjwal Solanki <75205571+ujjwalsolankii@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:11:13 +0530 Subject: [PATCH 101/149] docs: Updated README file (#29770) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f943581a9b1f..a63baba65dd0 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ yarn turbo run ms After initialized, you can access the server at http://localhost:4000 -> ⚠️ Check more detailed informations in the [Rocket.Chat Environment Setup](https://developer.rocket.chat/rocket.chat/rocket-chat-environment-setup) guide +> ⚠️ Check more detailed information in the [Rocket.Chat Environment Setup](https://developer.rocket.chat/rocket.chat/rocket-chat-environment-setup) guide # 💻 Installation From 3df4d266e4cb206bc61010a76d0acd312889ce21 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Sun, 9 Jul 2023 22:27:53 -0300 Subject: [PATCH 102/149] chore: bump fuselage packages --- yarn.lock | 155 ++++++++++++++++++++++++------------------------------ 1 file changed, 69 insertions(+), 86 deletions(-) diff --git a/yarn.lock b/yarn.lock index 49aef6230cd1..4f3bd5af46f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9601,7 +9601,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.103, @rocket.chat/css-in-js@npm:~0.31.23-dev.157": +"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.160": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9615,19 +9615,19 @@ __metadata: linkType: hard "@rocket.chat/css-in-js@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.103" + version: 0.31.23-dev.160 + resolution: "@rocket.chat/css-in-js@npm:0.31.23-dev.160" dependencies: "@emotion/hash": ^0.9.0 - "@rocket.chat/css-supports": ~0.31.23-dev.103 - "@rocket.chat/memo": ~0.31.23-dev.103 - "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.103 + "@rocket.chat/css-supports": ~0.31.23-dev.160 + "@rocket.chat/memo": ~0.31.23-dev.160 + "@rocket.chat/stylis-logical-props-middleware": ~0.31.23-dev.160 stylis: ~4.1.3 - checksum: 7fea932e9b60d186722f289989aead15fb6d8af2b11f75a44fefa3158c19d953ad789b161599b33bc5ede7c79c4d6de9b3453ca0dd1fd3ec453fe77891df4e55 + checksum: b7aad2a07fd37516e3b8d5ef8b5e61be24283c0c52731c0a0a5b09b4a254976eea741867acf5329446aa88d7f14a3344089cea637c777a8f741da59ef40392fd languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.103, @rocket.chat/css-supports@npm:~0.31.23-dev.157": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.160": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9703,9 +9703,9 @@ __metadata: linkType: soft "@rocket.chat/emitter@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/emitter@npm:0.31.23-dev.103" - checksum: 5163602cf6793678e1e8b1165fbf4c77a2d24f2536af87d3ceb8464da99ce9bff2ccede931bfd6fde9857890678b82f530b29b088ed41ddac7f4a60363e46f1b + version: 0.31.23-dev.160 + resolution: "@rocket.chat/emitter@npm:0.31.23-dev.160" + checksum: ddd5f86dee05445be8001498842021c974d755ba560e48c5c9d661cf59948f73fa823307b2829842d334d5b86924c9468a6ec0f704ad0e17895e64be5aa5cd3d languageName: node linkType: hard @@ -9796,33 +9796,21 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-hooks@npm:next": - version: 0.32.0-dev.274 - resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.274" +"@rocket.chat/fuselage-hooks@npm:next, @rocket.chat/fuselage-hooks@npm:~0.32.0-dev.299": + version: 0.32.0-dev.299 + resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.299" dependencies: use-sync-external-store: ~1.2.0 peerDependencies: "@rocket.chat/fuselage-tokens": "*" react: ^17.0.2 - checksum: 32e0872deb05e8b853aed59cab2cc4b3d3a707f0997009170a12b19d252ca09c203f8139b227c90678c0c06e14228c116ab428b9fba81228d1dab981844d8d54 - languageName: node - linkType: hard - -"@rocket.chat/fuselage-hooks@npm:~0.32.0-dev.242": - version: 0.32.0-dev.242 - resolution: "@rocket.chat/fuselage-hooks@npm:0.32.0-dev.242" - dependencies: - use-sync-external-store: ~1.2.0 - peerDependencies: - "@rocket.chat/fuselage-tokens": "*" - react: ^17.0.2 - checksum: f9dcfe53370b55c417668c32b08ddc81dcd6b8fbbc44d7968109906b3053985b34ee7d1ecdbea895b1d2eaaae7960fe2559cf0a6136799ea4725b986cb69895e + checksum: e83001544fe7118fc8dff2fa9a82dbeb2a4153f0d7c21b11dd332805326fe0433682d983914f42622807120bd1390acaf49957625216f357623ddf9e84a69170 languageName: node linkType: hard "@rocket.chat/fuselage-polyfills@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.103" + version: 0.31.23-dev.160 + resolution: "@rocket.chat/fuselage-polyfills@npm:0.31.23-dev.160" dependencies: "@juggle/resize-observer": ^3.4.0 clipboard-polyfill: ^3.0.3 @@ -9830,13 +9818,13 @@ __metadata: focus-visible: ^5.2.0 focus-within-polyfill: ^5.2.1 new-event-polyfill: ^1.0.1 - checksum: b4dbda8530718a9d585fbc38ed5d6efcb29d8df64982a7434117e11fc75df39c94faa666fd269cb1139d2431349e23fbb21dc5d7c100abb31f18eee27079d56c + checksum: 2fcd4c1720279898a2686753ac3978e23a8928ebcd93f05aca081324c8a602ddead41deacfb3a44c4ec5d426401c6960e3871723707a741e750a3c9040e5ceaf languageName: node linkType: hard "@rocket.chat/fuselage-toastbar@npm:next": - version: 0.32.0-dev.303 - resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.303" + version: 0.32.0-dev.360 + resolution: "@rocket.chat/fuselage-toastbar@npm:0.32.0-dev.360" peerDependencies: "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*" @@ -9844,21 +9832,14 @@ __metadata: "@rocket.chat/styled": "*" react: ^17.0.2 react-dom: ^17.0.2 - checksum: 479f5c50d1f6ce7a7e4cc4bbd0909f3bc1c3e83e3a9fdeb806d2b0540a17c46e30f8e72b816c954257411bd404f693d83bdf9b2499ce13cee2426d03c1dbec76 - languageName: node - linkType: hard - -"@rocket.chat/fuselage-tokens@npm:next": - version: 0.32.0-dev.280 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.280" - checksum: d8c7537512a7950ff6f0f3e23b3d6a2b82c0caea42d5b6e5599257fe2319cbd6f53a229c61413b025b20f576521aca67d9cba378f15e19edddae11a55133672a + checksum: b2372a9231cf3f7c4b7e37b6441f936c5c137cfb0d0f0c424ba42bcc10c07898b7d7e5b5feb508ce2ecf0806baedbaf101c61352391db563ceaef6f85d852da5 languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.333": - version: 0.32.0-dev.333 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.333" - checksum: dd19009e75e6154361be566ecea5f328902fdf25a10fdcc9ecd18076e424dd5f7d1a4b4c56b7679fe28e4a4ff77e32107d2f83a2c9c98f968ef6ff18f5de95db +"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.336": + version: 0.32.0-dev.336 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.336" + checksum: 2cf52211b3a3ee9a6111db46bc05918244190be7a6fef482011dccc0cc9a0777362dd91010e9df45c67b2c3bcaf00e35a7fbdddfdb5e7f683a19baa1fa8211d0 languageName: node linkType: hard @@ -9918,14 +9899,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.383 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.383" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.157 - "@rocket.chat/css-supports": ~0.31.23-dev.157 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.333 - "@rocket.chat/memo": ~0.31.23-dev.157 - "@rocket.chat/styled": ~0.31.23-dev.157 + version: 0.32.0-dev.386 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.386" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.160 + "@rocket.chat/css-supports": ~0.31.23-dev.160 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.336 + "@rocket.chat/memo": ~0.31.23-dev.160 + "@rocket.chat/styled": ~0.31.23-dev.160 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9937,7 +9918,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 110c59c012dd019a6edddba2a1df6c4ceff6f5f84d5b4db206656ac06349bd5889ddd8ce2ee475bd91e947e457d28382fe938233891b9c81047ac3452ceedf93 + checksum: 3753fe5939b7fdead82076b824959bc7ed537349f810c104432d2ab1f7aa166a374642664e82e2cdc9efa3d1b4e1807c5ed12777cf7e875de24fca4f0ed58149 languageName: node linkType: hard @@ -10025,9 +10006,9 @@ __metadata: linkType: soft "@rocket.chat/icons@npm:next": - version: 0.32.0-dev.362 - resolution: "@rocket.chat/icons@npm:0.32.0-dev.362" - checksum: 1c2096913e154d0db68b8ccc933294486ff06e250983809ce165f1497df05deec8b5165b47dbefdd013013b4cdf4a3a20f2ac191155b629e5ec4a7bcab5d8870 + version: 0.32.0-dev.368 + resolution: "@rocket.chat/icons@npm:0.32.0-dev.368" + checksum: 3d60bc3b234eb74e5f216df880dad588558b643ba47db3001e1196c796947c68739ded0dcbe1ee13336ab23540cb11789e5f2528fcd39e64f16b83e851fcbd7e languageName: node linkType: hard @@ -10045,14 +10026,14 @@ __metadata: linkType: soft "@rocket.chat/layout@npm:next": - version: 0.32.0-dev.213 - resolution: "@rocket.chat/layout@npm:0.32.0-dev.213" + version: 0.32.0-dev.269 + resolution: "@rocket.chat/layout@npm:0.32.0-dev.269" peerDependencies: "@rocket.chat/fuselage": "*" react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: 6a7e069f669fe7201e12ffb287064fe724e69294010b6cc426d4c5936f7e2c93cfe9ae92ee480e0ce7864cd5af37b5f9163dbdcefb850571adc3242ed897a5f1 + checksum: a5ffa877ef19b63154c1c65fc4fece7dd35a08ede4cd1cb88dca1820a8732f15a8b0838c470af08dcb7efc80d7fc17f0e44b4f8be2855707d7c5c7b2b1d441d5 languageName: node linkType: hard @@ -10153,19 +10134,19 @@ __metadata: linkType: soft "@rocket.chat/logo@npm:next": - version: 0.32.0-dev.279 - resolution: "@rocket.chat/logo@npm:0.32.0-dev.279" + version: 0.32.0-dev.336 + resolution: "@rocket.chat/logo@npm:0.32.0-dev.336" dependencies: - "@rocket.chat/fuselage-hooks": ~0.32.0-dev.242 - "@rocket.chat/styled": ~0.31.23-dev.103 + "@rocket.chat/fuselage-hooks": ~0.32.0-dev.299 + "@rocket.chat/styled": ~0.31.23-dev.160 peerDependencies: react: 17.0.2 react-dom: 17.0.2 - checksum: a79a80a89111633f2c325ace46dc951fe67df94eb192a4ed691e274e19e951f7b22a9a301108f3db4daaac7667f8b9f5f2535a123a0ab72e94796358678acaeb + checksum: ac7b8b84ea675a0e6edeb8b108dd2bfbf9877b08144b2d2426f0bf371c8269e446763252c6e3533bcf7a3bc4118805e45067ca768cc01341db267682a810187c languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.103, @rocket.chat/memo@npm:~0.31.23-dev.157": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.160": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -10173,18 +10154,18 @@ __metadata: linkType: hard "@rocket.chat/memo@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/memo@npm:0.31.23-dev.103" - checksum: 44b91bb7e02ffbeb2b094e5f283a1ddc3ca205f6ff01f5d044911b8cfc690b7e1472ee9f296de46bc84dcf2eb64d4c314813aa74d2076ec170ec1321f43145a6 + version: 0.31.23-dev.160 + resolution: "@rocket.chat/memo@npm:0.31.23-dev.160" + checksum: 9179e92af5cb662dcb9d8db3d9b0309fb415d5e82ba663bd689105a2a7ada14ca6c7630d3b509e982b2096949e1f4f9fb51afc821e0481dc5563f6c3d5a8257e languageName: node linkType: hard "@rocket.chat/message-parser@npm:next": - version: 0.32.0-dev.296 - resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.296" + version: 0.32.0-dev.334 + resolution: "@rocket.chat/message-parser@npm:0.32.0-dev.334" dependencies: tldts: ~5.7.112 - checksum: 2a40090b0b5b94e531da3bd1433c6f60b98f19b85eb9ca81db8706fcf360d8245c97fde2257a6c39e9be7fa4a630eede5abf679847ef3c923594c7156f8eb334 + checksum: bdbb288f963e72152554ee4af3ae2e57f7c3955c083065b202246286a91f3439fe0eb9a75a5c3b2bea889ac0e98750deaf4b3e95e170ba969ab2a42cf21d9f61 languageName: node linkType: hard @@ -10663,8 +10644,8 @@ __metadata: linkType: soft "@rocket.chat/onboarding-ui@npm:next": - version: 0.32.0-dev.329 - resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.329" + version: 0.32.0-dev.386 + resolution: "@rocket.chat/onboarding-ui@npm:0.32.0-dev.386" dependencies: i18next: ~21.6.16 react-hook-form: ~7.27.1 @@ -10679,7 +10660,7 @@ __metadata: react: 17.0.2 react-dom: 17.0.2 react-i18next: ~11.15.4 - checksum: 3118c3f3bb91db6e30748a1bf031221b2459b611c260446adf5bafcc9e38fa600a8a3366567a88183b2e93286b538233cb585f0d414b53f02e2eb48b8b2ba3ad + checksum: a919f076b7b27f79628fc3f1a63beb6abe6352a19be4d187742f656255670c757326f1732094628d381727b026e4e8f94ba4b900878e028bed927233825f0aba languageName: node linkType: hard @@ -10956,22 +10937,22 @@ __metadata: linkType: soft "@rocket.chat/string-helpers@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.103" - checksum: b505564152613d5871dae226b67458d69fbacf2ddeb3f6d10cf81124ccccfd75ad60133bc772db6bf20f4b17f7cfe09fc0ea9c846c2f519b031ce22d0da6688e + version: 0.31.23-dev.160 + resolution: "@rocket.chat/string-helpers@npm:0.31.23-dev.160" + checksum: abe26244891bb29243cf8a7db66cdcb44e80a0c6b6e1d38e56efaa1e6bdd5a61f818398473dec1cfdd1f1941dca8b03e641b7ec3460715800a79ae37bd2545f6 languageName: node linkType: hard "@rocket.chat/styled@npm:next": - version: 0.31.23-dev.103 - resolution: "@rocket.chat/styled@npm:0.31.23-dev.103" + version: 0.31.23-dev.160 + resolution: "@rocket.chat/styled@npm:0.31.23-dev.160" dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.103 - checksum: aadeb2ffe37e01d694012ec49d301a9a8960b82377fc1daa2c332b657414c4c52839010d3f8ce5114312065b8da0cc41b70bdc93fa1916169c53d333d56428bc + "@rocket.chat/css-in-js": ~0.31.23-dev.160 + checksum: 5e64b9ff9e3cfcd0a885e92071d69dfd6bd46c6d0f23c5490b8bd93cf8c0254e41d83e708e9d523cadb89e84ce789d0d4757d810f38dd7a718375c875e51670d languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.103, @rocket.chat/styled@npm:~0.31.23-dev.157": +"@rocket.chat/styled@npm:~0.31.23-dev.160": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: @@ -10980,7 +10961,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/stylis-logical-props-middleware@npm:^0.31.23, @rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.103": +"@rocket.chat/stylis-logical-props-middleware@npm:^0.31.23, @rocket.chat/stylis-logical-props-middleware@npm:~0.31.23-dev.160": version: 0.31.23 resolution: "@rocket.chat/stylis-logical-props-middleware@npm:0.31.23" dependencies: @@ -11114,9 +11095,11 @@ __metadata: linkType: soft "@rocket.chat/ui-kit@npm:next": - version: 0.32.0-dev.294 - resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.294" - checksum: cf58890f3fbcfdff3df9436a33c4aa333de34369f89902107dee74c50c11b8af793c9c80a8df5046ffcabf0fd90111f31a6fc2030876e838e43ec4c6a5703fc4 + version: 0.32.0-dev.321 + resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.321" + peerDependencies: + "@rocket.chat/icons": "*" + checksum: 9e237ac9c3553455d2167ef945070fcdfa90c3403599cc0e6a0c0de6d5663851ce29f4cfc9e0b6809a711620431a2a9f69398041ddffcbc41fe99a90aa1ae483 languageName: node linkType: hard From c8462b7bc99f9f38ef7a055a95a2b840782e8a07 Mon Sep 17 00:00:00 2001 From: Pierre Lehnen <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:30:55 -0300 Subject: [PATCH 103/149] regression: option to start video conferences disappearing for regular users (#29763) --- apps/meteor/server/startup/migrations/index.ts | 1 + apps/meteor/server/startup/migrations/v301.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 apps/meteor/server/startup/migrations/v301.ts diff --git a/apps/meteor/server/startup/migrations/index.ts b/apps/meteor/server/startup/migrations/index.ts index f85acdf5aa93..04aa38e6af9b 100644 --- a/apps/meteor/server/startup/migrations/index.ts +++ b/apps/meteor/server/startup/migrations/index.ts @@ -34,4 +34,5 @@ import './v297'; import './v298'; import './v299'; import './v300'; +import './v301'; import './xrun'; diff --git a/apps/meteor/server/startup/migrations/v301.ts b/apps/meteor/server/startup/migrations/v301.ts new file mode 100644 index 000000000000..7cf1bb01d884 --- /dev/null +++ b/apps/meteor/server/startup/migrations/v301.ts @@ -0,0 +1,10 @@ +import { Permissions } from '@rocket.chat/models'; + +import { addMigration } from '../../lib/migrations'; + +addMigration({ + version: 301, + async up() { + await Permissions.updateOne({ _id: 'call-management', roles: { $ne: 'user' } }, { $addToSet: { roles: 'user' } }); + }, +}); From 359338a1204601055ff546322cfb3350f571e8bd Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Tue, 11 Jul 2023 00:06:50 +0530 Subject: [PATCH 104/149] fix: Prevent app's bridges from overriding lastMsg prop on rooms col (#29756) Co-authored-by: Gabriel Casals <83978645+casalsgh@users.noreply.github.com> Co-authored-by: Kevin Aleman <kaleman960@gmail.com> --- .changeset/dirty-crabs-deliver.md | 5 +++++ .../app/apps/server/converters/rooms.js | 19 ++++++++++++++++++- .../server/hooks/saveLastVisitorMessageTs.ts | 3 +++ packages/core-typings/src/IInquiry.ts | 3 +++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/dirty-crabs-deliver.md diff --git a/.changeset/dirty-crabs-deliver.md b/.changeset/dirty-crabs-deliver.md new file mode 100644 index 000000000000..374d10e37530 --- /dev/null +++ b/.changeset/dirty-crabs-deliver.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: Prevent app's bridges from overriding the lastMsg prop which further was affecting Omni-Visitor abandonment feature for app diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 8fc74ac21c9a..44d513bd591d 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -38,11 +38,16 @@ export class AppRoomsConverter { let v; if (room.visitor) { const visitor = await LivechatVisitors.findOneById(room.visitor.id); + + const lastMessageTs = room?.visitor?.lastMessageTs; + const phone = room?.visitor?.channelPhone; v = { _id: visitor._id, username: visitor.username, token: visitor.token, status: visitor.status || 'online', + ...(lastMessageTs && { lastMessageTs }), + ...(phone && { phone }), }; } @@ -172,9 +177,21 @@ export class AppRoomsConverter { return undefined; } + const { lastMessageTs, phone } = v; + delete room.v; - return this.orch.getConverters().get('visitors').convertById(v._id); + return { + ...(await this.orch.getConverters().get('visitors').convertById(v._id)), + // Note: room.v is not just visitor, it also contains channel related visitor data + // so we need to pass this data to the converter + // So suppose you have a contact whom we're contacting using SMS via 2 phone no's, + // let's call X and Y. Then if the contact sends a message using X phone number, + // then room.v.phoneNo would be X and correspondingly we'll store the timestamp of + // the last message from this visitor from X phone no on room.v.lastMessageTs + ...(phone && { channelPhone: phone }), + ...(lastMessageTs && { lastMessageTs }), + }; }, department: async (room) => { const { departmentId } = room; diff --git a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts index b6273c0f3e74..1dabee3b8c03 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts +++ b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts @@ -9,6 +9,9 @@ callbacks.add( if (!(isOmnichannelRoom(room) && room.v.token)) { return message; } + if (message.t) { + return message; + } if (message.token) { await LivechatRooms.setVisitorLastMessageTimestampByRoomId(room._id, message.ts); } diff --git a/packages/core-typings/src/IInquiry.ts b/packages/core-typings/src/IInquiry.ts index 26ba2bc32188..f3dba1e9d907 100644 --- a/packages/core-typings/src/IInquiry.ts +++ b/packages/core-typings/src/IInquiry.ts @@ -18,6 +18,9 @@ export enum LivechatInquiryStatus { OPEN = 'open', } +// This is a subset of the IVisitor interface + channel related fields +// IMPORTANT: If you're adding a new field here, make sure to update the +// apps-engine's room converter to include it too export interface IVisitor { _id: string; username: string; From 5d3c85ff15d153856d21e40562d6dfb5ce455173 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Mon, 10 Jul 2023 15:37:06 -0300 Subject: [PATCH 105/149] test: create mock package (#29765) --- packages/mock-providers/.eslintrc.json | 4 + packages/mock-providers/package.json | 31 ++++ .../src/MockedAuthorizationContext.tsx | 26 +++ .../src/MockedServerContext.tsx | 42 +++++ .../src/MockedSettingsContext.tsx | 42 +++++ .../src/MockedUiKitActionManager.tsx | 0 .../mock-providers/src/MockedUserContext.tsx | 48 ++++++ packages/mock-providers/tsconfig.json | 9 + yarn.lock | 154 +++++++++++++++++- 9 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 packages/mock-providers/.eslintrc.json create mode 100644 packages/mock-providers/package.json create mode 100644 packages/mock-providers/src/MockedAuthorizationContext.tsx create mode 100644 packages/mock-providers/src/MockedServerContext.tsx create mode 100644 packages/mock-providers/src/MockedSettingsContext.tsx create mode 100644 packages/mock-providers/src/MockedUiKitActionManager.tsx create mode 100644 packages/mock-providers/src/MockedUserContext.tsx create mode 100644 packages/mock-providers/tsconfig.json diff --git a/packages/mock-providers/.eslintrc.json b/packages/mock-providers/.eslintrc.json new file mode 100644 index 000000000000..a83aeda48e66 --- /dev/null +++ b/packages/mock-providers/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["@rocket.chat/eslint-config"], + "ignorePatterns": ["**/dist"] +} diff --git a/packages/mock-providers/package.json b/packages/mock-providers/package.json new file mode 100644 index 000000000000..9cbe0db45962 --- /dev/null +++ b/packages/mock-providers/package.json @@ -0,0 +1,31 @@ +{ + "name": "@rocket.chat/mock-providers", + "version": "0.0.1", + "private": true, + "devDependencies": { + "@rocket.chat/ui-contexts": "workspace:*", + "@tanstack/react-query": "^4.16.1", + "@types/jest": "^27.4.1", + "eslint": "^8.12.0", + "jest": "~29.5.0", + "react": "~17.0.2", + "ts-jest": "~29.0.5", + "typescript": "~5.0.2" + }, + "peerDependencies": { + "@tanstack/react-query": "*", + "react": "*" + }, + "scripts": { + "lint": "eslint --ext .js,.jsx,.ts,.tsx .", + "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", + "test": "jest", + "build": "rm -rf dist && tsc -p tsconfig.json", + "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput" + }, + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", + "files": [ + "/dist" + ] +} diff --git a/packages/mock-providers/src/MockedAuthorizationContext.tsx b/packages/mock-providers/src/MockedAuthorizationContext.tsx new file mode 100644 index 000000000000..18f791f1dd98 --- /dev/null +++ b/packages/mock-providers/src/MockedAuthorizationContext.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { AuthorizationContext } from '@rocket.chat/ui-contexts'; + +export const MockedAuthorizationContext = ({ permissions = [], children }: { permissions: string[]; children: React.ReactNode }) => { + return ( + <AuthorizationContext.Provider + value={{ + queryPermission: (id: string) => [() => (): void => undefined, (): boolean => permissions.includes(id)], + queryAtLeastOnePermission: () => [() => (): void => undefined, (): boolean => false], + queryAllPermissions: () => [() => (): void => undefined, (): boolean => false], + queryRole: () => [() => (): void => undefined, (): boolean => false], + roleStore: { + roles: {}, + emit: (): void => undefined, + on: () => (): void => undefined, + off: (): void => undefined, + events: (): 'change'[] => ['change'], + has: (): boolean => false, + once: () => (): void => undefined, + }, + }} + > + {children} + </AuthorizationContext.Provider> + ); +}; diff --git a/packages/mock-providers/src/MockedServerContext.tsx b/packages/mock-providers/src/MockedServerContext.tsx new file mode 100644 index 000000000000..f3c13004ed77 --- /dev/null +++ b/packages/mock-providers/src/MockedServerContext.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import type { Serialized } from '@rocket.chat/core-typings'; +import type { Method, OperationParams, OperationResult, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; +import type { ServerMethodName, ServerMethodParameters } from '@rocket.chat/ui-contexts'; +import { ServerContext } from '@rocket.chat/ui-contexts'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +export const MockedServerContext = ({ + handleRequest, + children, +}: { + handleRequest: <TMethod extends Method, TPathPattern extends PathPattern>(args: { + method: TMethod; + pathPattern: TPathPattern; + keys: UrlParams<TPathPattern>; + params: OperationParams<TMethod, TPathPattern>; + }) => Promise<Serialized<OperationResult<TMethod, TPathPattern>>>; + children: React.ReactNode; +}): any => { + const [queryClient] = React.useState(() => new QueryClient()); + return ( + <ServerContext.Provider + value={ + { + absoluteUrl: (path: string) => `http://localhost:3000/${path}`, + callMethod: <MethodName extends ServerMethodName>(_methodName: MethodName, ..._args: ServerMethodParameters<MethodName>) => + Promise.reject('mock not implemented'), + callEndpoint: async <TMethod extends Method, TPathPattern extends PathPattern>(args: { + method: TMethod; + pathPattern: TPathPattern; + keys: UrlParams<TPathPattern>; + params: OperationParams<TMethod, TPathPattern>; + }) => { + return handleRequest(args); + }, + } as any + } + > + <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> + </ServerContext.Provider> + ); +}; diff --git a/packages/mock-providers/src/MockedSettingsContext.tsx b/packages/mock-providers/src/MockedSettingsContext.tsx new file mode 100644 index 000000000000..f916f4a6fb47 --- /dev/null +++ b/packages/mock-providers/src/MockedSettingsContext.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import type { ISetting } from '@rocket.chat/core-typings'; +import { SettingsContext } from '@rocket.chat/ui-contexts'; +import type { ContextType } from 'react'; + +const settingContextValue: ContextType<typeof SettingsContext> = { + hasPrivateAccess: true, + isLoading: false, + querySetting: (_id: string) => [() => () => undefined, () => undefined], + querySettings: () => [() => () => undefined, () => []], + dispatch: async () => undefined, +}; + +const createSettingContextValue = ({ settings }: { settings?: Record<string, ISetting['value']> }): ContextType<typeof SettingsContext> => { + const cache = new Map<string, ISetting['value']>(); + + return { + ...settingContextValue, + ...(settings && { + querySetting: (_id: string) => [ + () => () => undefined, + () => { + if (cache.has(_id)) { + return cache.get(_id) as any; + } + cache.set(_id, { value: settings[_id] } as any); + return cache.get(_id) as any; + }, + ], + }), + }; +}; + +export const MockedSettingsContext = ({ + settings, + children, +}: { + children: React.ReactNode; + settings?: Record<string, ISetting['value']>; +}) => { + return <SettingsContext.Provider value={createSettingContextValue({ settings })}>{children}</SettingsContext.Provider>; +}; diff --git a/packages/mock-providers/src/MockedUiKitActionManager.tsx b/packages/mock-providers/src/MockedUiKitActionManager.tsx new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/mock-providers/src/MockedUserContext.tsx b/packages/mock-providers/src/MockedUserContext.tsx new file mode 100644 index 000000000000..8dd9ae53cf3f --- /dev/null +++ b/packages/mock-providers/src/MockedUserContext.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import type { LoginService } from '@rocket.chat/ui-contexts'; +import { UserContext } from '@rocket.chat/ui-contexts'; +import type { ContextType } from 'react'; + +const userContextValue: ContextType<typeof UserContext> = { + userId: 'john.doe', + user: { + _id: 'john.doe', + username: 'john.doe', + name: 'John Doe', + createdAt: new Date(), + active: true, + _updatedAt: new Date(), + roles: ['admin'], + type: 'user', + }, + queryPreference: (<T,>(pref: string, defaultValue: T) => [ + () => () => undefined, + () => (typeof pref === 'string' ? undefined : defaultValue), + ]) as any, + querySubscriptions: () => [() => () => undefined, () => []], + querySubscription: () => [() => () => undefined, () => undefined], + queryRoom: () => [() => () => undefined, () => undefined], + + queryAllServices: () => [() => (): void => undefined, (): LoginService[] => []], + loginWithService: () => () => Promise.reject('loginWithService not implemented'), + loginWithPassword: async () => Promise.reject('loginWithPassword not implemented'), + loginWithToken: async () => Promise.reject('loginWithToken not implemented'), + logout: () => Promise.resolve(), +}; + +const createUserContextValue = ({ userPreferences }: { userPreferences?: Record<string, unknown> }): ContextType<typeof UserContext> => { + return { + ...userContextValue, + ...(userPreferences && { queryPreference: (id) => [() => () => undefined, () => userPreferences[id as unknown as string] as any] }), + }; +}; + +export const MockedUserContext = ({ + userPreferences, + children, +}: { + children: React.ReactNode; + userPreferences?: Record<string, unknown>; +}) => { + return <UserContext.Provider value={createUserContextValue({ userPreferences })}>{children}</UserContext.Provider>; +}; diff --git a/packages/mock-providers/tsconfig.json b/packages/mock-providers/tsconfig.json new file mode 100644 index 000000000000..58749dfbb7e4 --- /dev/null +++ b/packages/mock-providers/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.client.json", + "compilerOptions": { + "jsx": "react", + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["./src/**/*"] +} diff --git a/yarn.lock b/yarn.lock index 4f3bd5af46f9..364eca70ff70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,13 @@ __metadata: version: 6 cacheKey: 8 +"@aashutoshrathi/word-wrap@npm:^1.2.3": + version: 1.2.6 + resolution: "@aashutoshrathi/word-wrap@npm:1.2.6" + checksum: ada901b9e7c680d190f1d012c84217ce0063d8f5c5a7725bb91ec3c5ed99bb7572680eb2d2938a531ccbaec39a95422fcd8a6b4a13110c7d98dd75402f66a0cd + languageName: node + linkType: hard + "@actions/core@npm:^1.10.0": version: 1.10.0 resolution: "@actions/core@npm:1.10.0" @@ -5002,6 +5009,23 @@ __metadata: languageName: node linkType: hard +"@eslint/eslintrc@npm:^2.1.0": + version: 2.1.0 + resolution: "@eslint/eslintrc@npm:2.1.0" + dependencies: + ajv: ^6.12.4 + debug: ^4.3.2 + espree: ^9.6.0 + globals: ^13.19.0 + ignore: ^5.2.0 + import-fresh: ^3.2.1 + js-yaml: ^4.1.0 + minimatch: ^3.1.2 + strip-json-comments: ^3.1.1 + checksum: d5ed0adbe23f6571d8c9bb0ca6edf7618dc6aed4046aa56df7139f65ae7b578874e0d9c796df784c25bda648ceb754b6320277d828c8b004876d7443b8dc018c + languageName: node + linkType: hard + "@eslint/js@npm:8.43.0": version: 8.43.0 resolution: "@eslint/js@npm:8.43.0" @@ -5009,6 +5033,13 @@ __metadata: languageName: node linkType: hard +"@eslint/js@npm:8.44.0": + version: 8.44.0 + resolution: "@eslint/js@npm:8.44.0" + checksum: fc539583226a28f5677356e9f00d2789c34253f076643d2e32888250e509a4e13aafe0880cb2425139051de0f3a48d25bfc5afa96b7304f203b706c17340e3cf + languageName: node + linkType: hard + "@faker-js/faker@npm:~8.0.2": version: 8.0.2 resolution: "@faker-js/faker@npm:8.0.2" @@ -10538,6 +10569,24 @@ __metadata: languageName: unknown linkType: soft +"@rocket.chat/mock-providers@workspace:packages/mock-providers": + version: 0.0.0-use.local + resolution: "@rocket.chat/mock-providers@workspace:packages/mock-providers" + dependencies: + "@rocket.chat/ui-contexts": "workspace:*" + "@tanstack/react-query": ^4.16.1 + "@types/jest": ^27.4.1 + eslint: ^8.12.0 + jest: ~29.5.0 + react: ~17.0.2 + ts-jest: ~29.0.5 + typescript: ~5.0.2 + peerDependencies: + "@tanstack/react-query": "*" + react: "*" + languageName: unknown + linkType: soft + "@rocket.chat/model-typings@workspace:^, @rocket.chat/model-typings@workspace:packages/model-typings": version: 0.0.0-use.local resolution: "@rocket.chat/model-typings@workspace:packages/model-typings" @@ -11063,7 +11112,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/ui-contexts@workspace:^, @rocket.chat/ui-contexts@workspace:packages/ui-contexts, @rocket.chat/ui-contexts@workspace:~": +"@rocket.chat/ui-contexts@workspace:*, @rocket.chat/ui-contexts@workspace:^, @rocket.chat/ui-contexts@workspace:packages/ui-contexts, @rocket.chat/ui-contexts@workspace:~": version: 0.0.0-use.local resolution: "@rocket.chat/ui-contexts@workspace:packages/ui-contexts" dependencies: @@ -15684,6 +15733,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.9.0": + version: 8.10.0 + resolution: "acorn@npm:8.10.0" + bin: + acorn: bin/acorn + checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d + languageName: node + linkType: hard + "add-px-to-style@npm:1.0.0": version: 1.0.0 resolution: "add-px-to-style@npm:1.0.0" @@ -22397,6 +22455,55 @@ __metadata: languageName: node linkType: hard +"eslint@npm:^8.12.0": + version: 8.44.0 + resolution: "eslint@npm:8.44.0" + dependencies: + "@eslint-community/eslint-utils": ^4.2.0 + "@eslint-community/regexpp": ^4.4.0 + "@eslint/eslintrc": ^2.1.0 + "@eslint/js": 8.44.0 + "@humanwhocodes/config-array": ^0.11.10 + "@humanwhocodes/module-importer": ^1.0.1 + "@nodelib/fs.walk": ^1.2.8 + ajv: ^6.10.0 + chalk: ^4.0.0 + cross-spawn: ^7.0.2 + debug: ^4.3.2 + doctrine: ^3.0.0 + escape-string-regexp: ^4.0.0 + eslint-scope: ^7.2.0 + eslint-visitor-keys: ^3.4.1 + espree: ^9.6.0 + esquery: ^1.4.2 + esutils: ^2.0.2 + fast-deep-equal: ^3.1.3 + file-entry-cache: ^6.0.1 + find-up: ^5.0.0 + glob-parent: ^6.0.2 + globals: ^13.19.0 + graphemer: ^1.4.0 + ignore: ^5.2.0 + import-fresh: ^3.0.0 + imurmurhash: ^0.1.4 + is-glob: ^4.0.0 + is-path-inside: ^3.0.3 + js-yaml: ^4.1.0 + json-stable-stringify-without-jsonify: ^1.0.1 + levn: ^0.4.1 + lodash.merge: ^4.6.2 + minimatch: ^3.1.2 + natural-compare: ^1.4.0 + optionator: ^0.9.3 + strip-ansi: ^6.0.1 + strip-json-comments: ^3.1.0 + text-table: ^0.2.0 + bin: + eslint: bin/eslint.js + checksum: d06309ce4aafb9d27d558c8e5e5aa5cba3bbec3ce8ceccbc7d4b7a35f2b67fd40189159155553270e2e6febeb69bd8a3b60d6241c8f5ddc2ef1702ccbd328501 + languageName: node + linkType: hard + "eslint@npm:^8.43.0, eslint@npm:~8.43.0": version: 8.43.0 resolution: "eslint@npm:8.43.0" @@ -22468,6 +22575,17 @@ __metadata: languageName: node linkType: hard +"espree@npm:^9.6.0": + version: 9.6.0 + resolution: "espree@npm:9.6.0" + dependencies: + acorn: ^8.9.0 + acorn-jsx: ^5.3.2 + eslint-visitor-keys: ^3.4.1 + checksum: 1287979510efb052a6a97c73067ea5d0a40701b29adde87bbe2d3eb1667e39ca55e8129e20e2517fed3da570150e7ef470585228459a8f3e3755f45007a1c662 + languageName: node + linkType: hard + "esprima@npm:^4.0.0, esprima@npm:^4.0.1": version: 4.0.1 resolution: "esprima@npm:4.0.1" @@ -32272,6 +32390,20 @@ __metadata: languageName: node linkType: hard +"optionator@npm:^0.9.3": + version: 0.9.3 + resolution: "optionator@npm:0.9.3" + dependencies: + "@aashutoshrathi/word-wrap": ^1.2.3 + deep-is: ^0.1.3 + fast-levenshtein: ^2.0.6 + levn: ^0.4.1 + prelude-ls: ^1.2.1 + type-check: ^0.4.0 + checksum: 09281999441f2fe9c33a5eeab76700795365a061563d66b098923eb719251a42bdbe432790d35064d0816ead9296dbeb1ad51a733edf4167c96bd5d0882e428a + languageName: node + linkType: hard + "optipng-bin@npm:^7.0.0": version: 7.0.1 resolution: "optipng-bin@npm:7.0.1" @@ -40251,6 +40383,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:~5.0.2": + version: 5.0.4 + resolution: "typescript@npm:5.0.4" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 82b94da3f4604a8946da585f7d6c3025fff8410779e5bde2855ab130d05e4fd08938b9e593b6ebed165bda6ad9292b230984f10952cf82f0a0ca07bbeaa08172 + languageName: node + linkType: hard + "typescript@patch:typescript@^5.1.3#~builtin<compat/typescript>, typescript@patch:typescript@~5.1.3#~builtin<compat/typescript>": version: 5.1.3 resolution: "typescript@patch:typescript@npm%3A5.1.3#~builtin<compat/typescript>::version=5.1.3&hash=f456af" @@ -40261,6 +40403,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@~5.0.2#~builtin<compat/typescript>": + version: 5.0.4 + resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin<compat/typescript>::version=5.0.4&hash=f456af" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 6a1fe9a77bb9c5176ead919cc4a1499ee63e46b4e05bf667079f11bf3a8f7887f135aa72460a4c3b016e6e6bb65a822cb8689a6d86cbfe92d22cc9f501f09213 + languageName: node + linkType: hard + "ua-parser-js@npm:^1.0.35": version: 1.0.35 resolution: "ua-parser-js@npm:1.0.35" From 38d1003842293bf08c0e94a632ffaeb8963bbe3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:01:26 -0300 Subject: [PATCH 106/149] regression: add missing translations on MenuV2 replace (#29777) --- .../header/actions/hooks/useAdministrationMenu.tsx | 9 +++++---- .../sidebar/header/actions/hooks/useCreateRoomMenu.tsx | 7 ++++--- .../sidebar/header/actions/hooks/useSortMenu.tsx | 10 +++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx index 5145ac30c0f4..f6179bb93d26 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx @@ -1,5 +1,5 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useAtLeastOnePermission, usePermission } from '@rocket.chat/ui-contexts'; +import { useAtLeastOnePermission, usePermission, useTranslation } from '@rocket.chat/ui-contexts'; import { AccountBox } from '../../../../../app/ui-utils/client'; import type { IAppAccountBoxItem, AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; @@ -39,6 +39,7 @@ const ADMIN_PERMISSIONS = [ ]; export const useAdministrationMenu = () => { + const t = useTranslation(); const getAccountBoxItems = useMutableCallback(() => AccountBox.getItems()); const accountBoxItems = useReactiveValue(getAccountBoxItems); @@ -66,9 +67,9 @@ export const useAdministrationMenu = () => { const auditItems = useAuditItems({ showAudit: hasAuditPermission, showAuditLog: hasAuditLogPermission }); const sections = [ - { title: 'Administration', items: administrationItems, permission: showAdmin }, - { title: 'Apps', items: appItems, permission: showApps }, - { title: 'Audit', items: auditItems, permission: showAudit }, + { title: t('Administration'), items: administrationItems, permission: showAdmin }, + { title: t('Apps'), items: appItems, permission: showApps }, + { title: t('Audit'), items: auditItems, permission: showAudit }, ]; return sections.filter(({ permission }) => permission); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx index 7e4cbe8d138e..f220267fac05 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useCreateRoomMenu.tsx @@ -1,4 +1,4 @@ -import { useAtLeastOnePermission, useSetting } from '@rocket.chat/ui-contexts'; +import { useAtLeastOnePermission, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import { useIsEnterprise } from '../../../../hooks/useIsEnterprise'; import { useCreateRoomItems } from './useCreateRoomItems'; @@ -7,6 +7,7 @@ import { useMatrixFederationItems } from './useMatrixFederationItems.tsx'; const CREATE_ROOM_PERMISSIONS = ['create-c', 'create-p', 'create-d', 'start-discussion', 'start-discussion-other-user']; export const useCreateRoom = () => { + const t = useTranslation(); const showCreate = useAtLeastOnePermission(CREATE_ROOM_PERMISSIONS); const { data } = useIsEnterprise(); @@ -16,8 +17,8 @@ export const useCreateRoom = () => { const matrixFederationSearchItems = useMatrixFederationItems({ isMatrixEnabled }); const sections = [ - { title: 'Create_new', items: createRoomItems, permission: showCreate }, - { title: 'Explore', items: matrixFederationSearchItems, permission: showCreate && isMatrixEnabled }, + { title: t('Create_new'), items: createRoomItems, permission: showCreate }, + { title: t('Explore'), items: matrixFederationSearchItems, permission: showCreate && isMatrixEnabled }, ]; return sections.filter((section) => section.permission); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx index 8a3f6a56e590..bea1d999997e 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useSortMenu.tsx @@ -1,16 +1,20 @@ +import { useTranslation } from '@rocket.chat/ui-contexts'; + import { useGroupingListItems } from './useGroupingListItems'; import { useSortModeItems } from './useSortModeItems'; import { useViewModeItems } from './useViewModeItems'; export const useSortMenu = () => { + const t = useTranslation(); + const viewModeItems = useViewModeItems(); const sortModeItems = useSortModeItems(); const groupingListItems = useGroupingListItems(); const sections = [ - { title: 'Display', items: viewModeItems }, - { title: 'Sort_By', items: sortModeItems }, - { title: 'Group_by', items: groupingListItems }, + { title: t('Display'), items: viewModeItems }, + { title: t('Sort_By'), items: sortModeItems }, + { title: t('Group_by'), items: groupingListItems }, ]; return sections; From cadec3a504e57a4535244abc183181a3869b9b5d Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Mon, 10 Jul 2023 22:24:36 -0300 Subject: [PATCH 107/149] chore: create FeaturePreview Component (#29759) Co-authored-by: Tasso Evangelista <tasso.evangelista@rocket.chat> --- .../components/message/toolbox/Toolbox.tsx | 2 +- .../hooks/useFeaturePreviewList.spec.tsx | 89 --- .../sidebar/header/hooks/useAccountItems.tsx | 2 +- .../AccountFeaturePreviewBadge.tsx | 3 +- .../AccountFeaturePreviewPage.tsx | 4 +- .../client/views/account/sidebarItems.tsx | 2 +- apps/meteor/jest.client.config.ts | 2 +- apps/meteor/package.json | 3 +- ee/packages/api-client/package.json | 4 +- ee/packages/ddp-client/package.json | 6 +- ee/packages/omnichannel-services/package.json | 4 +- ee/packages/pdf-worker/package.json | 8 +- ee/packages/presence/package.json | 2 +- ee/packages/ui-theming/package.json | 4 +- packages/account-utils/package.json | 4 +- packages/agenda/package.json | 4 +- packages/base64/package.json | 2 +- packages/cas-validate/package.json | 4 +- packages/core-services/package.json | 4 +- packages/cron/package.json | 4 +- packages/gazzodown/package.json | 6 +- packages/i18n/package.json | 4 +- packages/log-format/package.json | 4 +- packages/mock-providers/package.json | 4 +- packages/model-typings/package.json | 4 +- packages/models/package.json | 4 +- packages/node-poplib/package.json | 4 +- packages/random/package.json | 4 +- packages/rest-typings/package.json | 6 +- packages/server-fetch/package.json | 4 +- packages/sha256/package.json | 2 +- packages/tools/package.json | 4 +- packages/ui-client/jest.config.ts | 25 + packages/ui-client/package.json | 12 +- .../FeaturePreview/FeaturePreview.spec.tsx | 63 ++ .../FeaturePreview/FeaturePreview.tsx | 26 + .../src}/hooks/useFeaturePreview.spec.tsx | 135 ++-- .../ui-client/src}/hooks/useFeaturePreview.ts | 0 .../src/hooks/useFeaturePreviewList.spec.tsx | 73 ++ .../src}/hooks/useFeaturePreviewList.ts | 0 packages/ui-client/src/index.ts | 2 + packages/ui-client/tsconfig.json | 5 +- packages/ui-composer/package.json | 4 +- packages/ui-contexts/package.json | 4 +- packages/ui-video-conf/package.json | 4 +- packages/uikit-playground/package.json | 4 +- packages/web-ui-registration/package.json | 4 +- yarn.lock | 749 +++--------------- 48 files changed, 434 insertions(+), 883 deletions(-) delete mode 100644 apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx create mode 100644 packages/ui-client/jest.config.ts create mode 100644 packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx create mode 100644 packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx rename {apps/meteor/client => packages/ui-client/src}/hooks/useFeaturePreview.spec.tsx (63%) rename {apps/meteor/client => packages/ui-client/src}/hooks/useFeaturePreview.ts (100%) create mode 100644 packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx rename {apps/meteor/client => packages/ui-client/src}/hooks/useFeaturePreviewList.ts (100%) diff --git a/apps/meteor/client/components/message/toolbox/Toolbox.tsx b/apps/meteor/client/components/message/toolbox/Toolbox.tsx index 2c9a3ccae95b..ee1a0bc2c1b8 100644 --- a/apps/meteor/client/components/message/toolbox/Toolbox.tsx +++ b/apps/meteor/client/components/message/toolbox/Toolbox.tsx @@ -1,6 +1,7 @@ import type { IMessage, IRoom, ISubscription, ITranslatedMessage } from '@rocket.chat/core-typings'; import { isThreadMessage, isRoomFederated } from '@rocket.chat/core-typings'; import { MessageToolbox, MessageToolboxItem } from '@rocket.chat/fuselage'; +import { useFeaturePreview } from '@rocket.chat/ui-client'; import { useUser, useSettings, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; @@ -10,7 +11,6 @@ import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/M import { MessageAction } from '../../../../app/ui-utils/client/lib/MessageAction'; import { sdk } from '../../../../app/utils/client/lib/SDKClient'; import { useEmojiPickerData } from '../../../contexts/EmojiPickerContext'; -import { useFeaturePreview } from '../../../hooks/useFeaturePreview'; import EmojiElement from '../../../views/composer/EmojiPicker/EmojiElement'; import { useIsSelecting } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoTranslate'; diff --git a/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx b/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx deleted file mode 100644 index 2fbcdb4cb6d2..000000000000 --- a/apps/meteor/client/hooks/useFeaturePreviewList.spec.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import React from 'react'; - -import { MockedSettingsContext, MockedUserContext } from './useFeaturePreview.spec'; -import { useFeaturePreviewList, defaultFeaturesPreview } from './useFeaturePreviewList'; - -it('should return the number of unseen features and Accounts_AllowFeaturePreview enabled ', () => { - const { result } = renderHook( - () => { - return useFeaturePreviewList(); - }, - { - wrapper: ({ children }) => ( - <MockedSettingsContext - settings={{ - Accounts_AllowFeaturePreview: true, - }} - > - <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> - </MockedSettingsContext> - ), - }, - ); - - expect(result.all[0]).toEqual( - expect.objectContaining({ - featurePreviewEnabled: true, - unseenFeatures: defaultFeaturesPreview.length, - }), - ); -}); - -it('should return the number of unseen features and Accounts_AllowFeaturePreview disabled ', () => { - const { result } = renderHook( - () => { - return useFeaturePreviewList(); - }, - { - wrapper: ({ children }) => ( - <MockedSettingsContext - settings={{ - Accounts_AllowFeaturePreview: false, - }} - > - <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> - </MockedSettingsContext> - ), - }, - ); - - expect(result.all[0]).toEqual( - expect.objectContaining({ - featurePreviewEnabled: false, - unseenFeatures: 0, - }), - ); -}); - -it('should return 0 unseen features', () => { - const { result } = renderHook( - () => { - return useFeaturePreviewList(); - }, - { - wrapper: ({ children }) => ( - <MockedSettingsContext - settings={{ - Accounts_AllowFeaturePreview: true, - }} - > - <MockedUserContext - userPreferences={{ - featuresPreview: defaultFeaturesPreview, - }} - > - {children} - </MockedUserContext> - </MockedSettingsContext> - ), - }, - ); - - expect(result.all[0]).toEqual( - expect.objectContaining({ - featurePreviewEnabled: true, - unseenFeatures: 0, - }), - ); -}); diff --git a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx index 5e6f4c7d183a..e4757c2d48b1 100644 --- a/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx @@ -1,10 +1,10 @@ import { Badge } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { defaultFeaturesPreview, useFeaturePreviewList } from '@rocket.chat/ui-client'; import { useLogout, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; -import { useFeaturePreviewList, defaultFeaturesPreview } from '../../../hooks/useFeaturePreviewList'; export const useAccountItems = (): GenericMenuItemProps[] => { const t = useTranslation(); diff --git a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx index 631a2b76c625..95d7d2d12353 100644 --- a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx +++ b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewBadge.tsx @@ -1,9 +1,8 @@ import { Badge } from '@rocket.chat/fuselage'; +import { useFeaturePreviewList } from '@rocket.chat/ui-client'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; -import { useFeaturePreviewList } from '../../../hooks/useFeaturePreviewList'; - const AccountFeaturePreviewBadge = () => { const t = useTranslation(); const { unseenFeatures } = useFeaturePreviewList(); diff --git a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx index 414aa2377490..d190fd00dc17 100644 --- a/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx +++ b/apps/meteor/client/views/account/featurePreview/AccountFeaturePreviewPage.tsx @@ -11,6 +11,8 @@ import { StatesTitle, Accordion, } from '@rocket.chat/fuselage'; +import type { FeaturePreviewProps } from '@rocket.chat/ui-client'; +import { useFeaturePreviewList } from '@rocket.chat/ui-client'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import type { ChangeEvent } from 'react'; @@ -18,8 +20,6 @@ import React, { useEffect, Fragment } from 'react'; import { useForm } from 'react-hook-form'; import Page from '../../../components/Page'; -import type { FeaturePreviewProps } from '../../../hooks/useFeaturePreviewList'; -import { useFeaturePreviewList } from '../../../hooks/useFeaturePreviewList'; const AccountFeaturePreviewPage = () => { const t = useTranslation(); diff --git a/apps/meteor/client/views/account/sidebarItems.tsx b/apps/meteor/client/views/account/sidebarItems.tsx index 4087774d98cc..a2d0720fe6e2 100644 --- a/apps/meteor/client/views/account/sidebarItems.tsx +++ b/apps/meteor/client/views/account/sidebarItems.tsx @@ -1,8 +1,8 @@ +import { defaultFeaturesPreview } from '@rocket.chat/ui-client'; import React from 'react'; import { hasPermission, hasAtLeastOnePermission } from '../../../app/authorization/client'; import { settings } from '../../../app/settings/client'; -import { defaultFeaturesPreview } from '../../hooks/useFeaturePreviewList'; import { createSidebarItems } from '../../lib/createSidebarItems'; import AccountFeaturePreviewBadge from './featurePreview/AccountFeaturePreviewBadge'; diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.client.config.ts index 9523d726d9e9..db3a03ecf03f 100644 --- a/apps/meteor/jest.client.config.ts +++ b/apps/meteor/jest.client.config.ts @@ -3,7 +3,7 @@ export default { testEnvironment: 'jsdom', modulePathIgnorePatterns: ['<rootDir>/dist/'], - testMatch: ['<rootDir>/client/hooks/**.spec.[jt]s?(x)', './client/hooks/**.spec.ts', '/client/hooks/**.spec.ts'], + testMatch: ['<rootDir>/client/hooks/**.spec.[jt]s?(x)', '<rootDir>/client/components/**.spec.[jt]s?(x)'], transform: { '^.+\\.(t|j)sx?$': '@swc/jest', }, diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 80289d401c38..99385d0b56f3 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -73,6 +73,7 @@ "@playwright/test": "^1.22.2", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/livechat": "workspace:^", + "@rocket.chat/mock-providers": "workspace:^", "@settlin/spacebars-loader": "^1.0.9", "@storybook/addon-essentials": "~6.5.16", "@storybook/addon-interactions": "~6.5.16", @@ -177,7 +178,7 @@ "eslint-plugin-you-dont-need-lodash-underscore": "~6.12.0", "fast-glob": "^3.2.12", "i18next": "^20.6.1", - "jest": "^29.6.1", + "jest": "~29.6.1", "jsdom-global": "^3.0.2", "mocha": "^9.2.2", "nyc": "^15.1.0", diff --git a/ee/packages/api-client/package.json b/ee/packages/api-client/package.json index f6c673ce22ff..f876bc176cb2 100644 --- a/ee/packages/api-client/package.json +++ b/ee/packages/api-client/package.json @@ -4,10 +4,10 @@ "devDependencies": { "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/strict-uri-encode": "^2.0.0", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "jest-fetch-mock": "^3.0.3", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/ee/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json index 8a8c73c84436..aa24abd7ead5 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -4,11 +4,11 @@ "devDependencies": { "@swc/core": "^1.3.66", "@swc/jest": "^0.2.26", - "@types/jest": "^29.5.2", + "@types/jest": "~29.5.3", "@types/ws": "^8.5.5", "eslint": "^8.43.0", - "jest": "^29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "jest-websocket-mock": "^2.4.0", "typescript": "~5.1.3", "ws": "^8.13.0" diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index ded11e4f6da8..3978a5090a6f 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -4,9 +4,9 @@ "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index 95d4c834eaf1..f6d21946ed8d 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -8,12 +8,12 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "~13.4.0", "@types/emojione": "^2.2.6", - "@types/jest": "~29.5.2", - "@types/react-dom": "^18.2.5", + "@types/jest": "~29.5.3", + "@types/react-dom": "~17.0.20", "@types/testing-library__jest-dom": "~5.14.6", "eslint": "~8.43.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "react-dom": "^18.2.0", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 3d9f0602ffa4..637170c811a3 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -12,7 +12,7 @@ "@types/node": "^14.18.51", "babel-jest": "^29.0.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "typescript": "~5.1.3" }, "scripts": { diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index b16a3c816849..e75c37bba089 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -18,14 +18,14 @@ "@storybook/manager-webpack4": "~6.5.16", "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/react": "~17.0.62", "eslint": "~8.43.0", "eslint-plugin-anti-trojan-source": "~1.1.1", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-testing-library": "^5.11.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "react": "~17.0.2", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", diff --git a/packages/account-utils/package.json b/packages/account-utils/package.json index 235f0314647b..d55c4835098b 100644 --- a/packages/account-utils/package.json +++ b/packages/account-utils/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/agenda/package.json b/packages/agenda/package.json index 434566005aee..c5b92a2ce430 100644 --- a/packages/agenda/package.json +++ b/packages/agenda/package.json @@ -13,9 +13,9 @@ }, "devDependencies": { "@types/debug": "^4.1.8", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/base64/package.json b/packages/base64/package.json index cf8d03b65c04..744c101512e7 100644 --- a/packages/base64/package.json +++ b/packages/base64/package.json @@ -19,7 +19,7 @@ "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/cas-validate/package.json b/packages/cas-validate/package.json index 2ee9412e5665..ee34be0210d1 100644 --- a/packages/cas-validate/package.json +++ b/packages/cas-validate/package.json @@ -4,9 +4,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/core-services/package.json b/packages/core-services/package.json index e0292eeb4f3c..3ef8d4bdb8bc 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -9,10 +9,10 @@ "@rocket.chat/eslint-config": "workspace:^", "@types/babel__core": "^7", "@types/babel__preset-env": "^7", - "@types/jest": "^29.5.1", + "@types/jest": "~29.5.3", "babel-jest": "^29.5.0", "eslint": "~8.43.0", - "jest": "^29.5.0", + "jest": "~29.6.1", "mongodb": "^4.12.1", "prettier": "~2.8.8", "typescript": "~5.1.3" diff --git a/packages/cron/package.json b/packages/cron/package.json index 9f009d83baa6..b6e1a006e59d 100644 --- a/packages/cron/package.json +++ b/packages/cron/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index cb195dd48654..46b0d6396122 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -25,7 +25,7 @@ "@swc/jest": "^0.2.26", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "~12.1.5", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/katex": "~0.16.0", "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", @@ -39,8 +39,8 @@ "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", "identity-obj-proxy": "^3.0.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "katex": "~0.16.7", "outdent": "^0.8.0", "react-docgen-typescript-plugin": "~1.0.5", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index bc913f9e10b1..020b4aae293b 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -8,10 +8,10 @@ "@babel/preset-typescript": "~7.22.5", "@types/babel__core": "~7.20.1", "@types/babel__preset-env": "~7.9.2", - "@types/jest": "^29.5.2", + "@types/jest": "~29.5.3", "babel-jest": "^29.5.0", "eslint": "^8.43.0", - "jest": "^29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "tsup": "^6.7.0", "typescript": "~5.1.3" diff --git a/packages/log-format/package.json b/packages/log-format/package.json index 497a3913a8f9..c3b0a1aa404a 100644 --- a/packages/log-format/package.json +++ b/packages/log-format/package.json @@ -5,9 +5,9 @@ "devDependencies": { "@types/chalk": "^2.2.0", "@types/ejson": "^2.2.0", - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/mock-providers/package.json b/packages/mock-providers/package.json index 9cbe0db45962..1251cde3399f 100644 --- a/packages/mock-providers/package.json +++ b/packages/mock-providers/package.json @@ -5,9 +5,9 @@ "devDependencies": { "@rocket.chat/ui-contexts": "workspace:*", "@tanstack/react-query": "^4.16.1", - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.12.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "react": "~17.0.2", "ts-jest": "~29.0.5", "typescript": "~5.0.2" diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index 5f4b0a104ede..b167704d770f 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -3,10 +3,10 @@ "version": "0.0.4", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/node-rsa": "^1.1.1", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "mongodb": "^4.12.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/models/package.json b/packages/models/package.json index 1a7e6cf3acc6..d69d02185c13 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -3,9 +3,9 @@ "version": "0.0.4", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/node-poplib/package.json b/packages/node-poplib/package.json index 0d51c8c9f1a0..879a6345e484 100644 --- a/packages/node-poplib/package.json +++ b/packages/node-poplib/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/random/package.json b/packages/random/package.json index f436db859d99..297e2b72acff 100644 --- a/packages/random/package.json +++ b/packages/random/package.json @@ -20,8 +20,8 @@ "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", "eslint": "~8.43.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 2c58dc3fd395..470e2c756aa7 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -3,10 +3,10 @@ "version": "6.2.9", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", - "jest-environment-jsdom": "~29.5.0", + "jest": "~29.6.1", + "jest-environment-jsdom": "~29.6.1", "mongodb": "^4.12.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/server-fetch/package.json b/packages/server-fetch/package.json index 35c1a5a0747f..e79d03be02b0 100644 --- a/packages/server-fetch/package.json +++ b/packages/server-fetch/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "^27.4.1", + "@types/jest": "~29.5.3", "eslint": "^8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/sha256/package.json b/packages/sha256/package.json index b57662c664c3..e8dd62ab8d1c 100644 --- a/packages/sha256/package.json +++ b/packages/sha256/package.json @@ -19,7 +19,7 @@ "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/tools/package.json b/packages/tools/package.json index 833a627023ef..50bca996be6a 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "ts-jest": "~29.0.5", "typescript": "~5.1.3" }, diff --git a/packages/ui-client/jest.config.ts b/packages/ui-client/jest.config.ts new file mode 100644 index 000000000000..636d50c6a980 --- /dev/null +++ b/packages/ui-client/jest.config.ts @@ -0,0 +1,25 @@ +export default { + errorOnDeprecated: true, + + testEnvironment: 'jsdom', + modulePathIgnorePatterns: ['<rootDir>/dist/'], + testMatch: ['<rootDir>/src/**/**.spec.[jt]s?(x)'], + transform: { + '^.+\\.(t|j)sx?$': [ + '@swc/jest', + { + jsc: { + transform: { + react: { + runtime: 'automatic', + }, + }, + }, + }, + ], + }, + moduleNameMapper: { + '\\.css$': 'identity-obj-proxy', + '^react($|/.+)': '<rootDir>/../../node_modules/react$1', + }, +}; diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 8fbb4d6acd21..25dca29e286a 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -8,6 +8,7 @@ "@rocket.chat/fuselage": "next", "@rocket.chat/fuselage-hooks": "next", "@rocket.chat/icons": "next", + "@rocket.chat/mock-providers": "workspace:^", "@rocket.chat/ui-contexts": "workspace:~", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", @@ -19,8 +20,11 @@ "@storybook/manager-webpack4": "~6.5.16", "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", + "@swc/jest": "^0.2.26", + "@testing-library/react": "^12.1.2", + "@testing-library/react-hooks": "^8.0.1", "@types/babel__core": "~7.20.1", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", "eslint": "~8.43.0", @@ -29,8 +33,9 @@ "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", "eslint-plugin-testing-library": "~5.11.0", - "jest": "~29.5.0", - "react": "~17.0.2", + "jest": "~29.6.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", "react-hook-form": "^7.30.0", "ts-jest": "~29.0.5", "typescript": "~5.1.3" @@ -39,6 +44,7 @@ "lint": "eslint --ext .js,.jsx,.ts,.tsx .", "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "test": "jest", + "testunit": "jest", "build": "rm -rf dist && tsc -p tsconfig-build.json", "storybook": "start-storybook -p 6006", "dev": "tsc -p tsconfig-build.json --watch --preserveWatchOutput" diff --git a/packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx new file mode 100644 index 000000000000..11c218855feb --- /dev/null +++ b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.spec.tsx @@ -0,0 +1,63 @@ +/* eslint-disable import/order */ +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from './FeaturePreview'; + +test('should renders off if the feature is disabled', async () => { + render( + <FeaturePreview feature='quickReactions'> + <FeaturePreviewOn>on</FeaturePreviewOn> + <FeaturePreviewOff>off</FeaturePreviewOff> + </FeaturePreview>, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: [], + }} + > + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(screen.getByText('off')).toBeInTheDocument(); +}); + +test('should renders on if the feature is enabled', async () => { + render( + <FeaturePreview feature='quickReactions'> + <FeaturePreviewOn>on</FeaturePreviewOn> + <FeaturePreviewOff>off</FeaturePreviewOff> + </FeaturePreview>, + { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: [{ name: 'quickReactions', value: true }], + }} + > + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }, + ); + + expect(screen.getByText('on')).toBeInTheDocument(); +}); diff --git a/packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx new file mode 100644 index 000000000000..09ec0700cb79 --- /dev/null +++ b/packages/ui-client/src/components/FeaturePreview/FeaturePreview.tsx @@ -0,0 +1,26 @@ +/* eslint-disable react/no-multi-comp */ +import type { ReactElement, ReactNode } from 'react'; +import { Children, Suspense, cloneElement } from 'react'; + +import { useFeaturePreview } from '../../hooks/useFeaturePreview'; +import { FeaturesAvailable } from '../../hooks/useFeaturePreviewList'; + +export const FeaturePreview = ({ feature, children }: { feature: FeaturesAvailable; children: ReactElement[] }) => { + const featureToggleEnabled = useFeaturePreview(feature); + + const toggledChildren = Children.map(children, (child) => + cloneElement(child, { + featureToggleEnabled, + }), + ); + + return <Suspense fallback={null}>{toggledChildren}</Suspense>; +}; + +export const FeaturePreviewOn = ({ children, featureToggleEnabled }: { children: ReactNode; featureToggleEnabled?: boolean }) => ( + <>{featureToggleEnabled && children}</> +); + +export const FeaturePreviewOff = ({ children, featureToggleEnabled }: { children: ReactNode; featureToggleEnabled?: boolean }) => ( + <>{!featureToggleEnabled && children}</> +); diff --git a/apps/meteor/client/hooks/useFeaturePreview.spec.tsx b/packages/ui-client/src/hooks/useFeaturePreview.spec.tsx similarity index 63% rename from apps/meteor/client/hooks/useFeaturePreview.spec.tsx rename to packages/ui-client/src/hooks/useFeaturePreview.spec.tsx index 0d0cbc5eca52..3afd8fdb0392 100644 --- a/apps/meteor/client/hooks/useFeaturePreview.spec.tsx +++ b/packages/ui-client/src/hooks/useFeaturePreview.spec.tsx @@ -45,96 +45,79 @@ const settingContextValue: ContextType<typeof SettingsContext> = { }; it('should return false if featurePreviewEnabled is false', () => { - const { result } = renderHook( - () => { - return useFeaturePreview('quickReactions'); - }, - { - wrapper: ({ children }) => ( - <MockedSettingsContext - settings={{ - Accounts_AllowFeaturePreview: false, - }} - > - <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> - </MockedSettingsContext> - ), - }, - ); + const { result } = renderHook(() => useFeaturePreview('quickReactions'), { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: false, + }} + > + <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> + </MockedSettingsContext> + ), + }); expect(result.all[0]).toBe(false); }); it('should return false if featurePreviewEnabled is true but feature is not in userPreferences', () => { - const { result } = renderHook( - () => { - return useFeaturePreview('quickReactions'); - }, - { - wrapper: ({ children }) => ( - <MockedSettingsContext - settings={{ - Accounts_AllowFeaturePreview: false, + const { result } = renderHook(() => useFeaturePreview('quickReactions'), { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: false, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: [ + { + name: 'quickReactions', + value: true, + }, + ], }} > - <MockedUserContext - userPreferences={{ - featuresPreview: [ - { - name: 'quickReactions', - value: true, - }, - ], - }} - > - {children} - </MockedUserContext> - </MockedSettingsContext> - ), - }, - ); + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }); expect(result.all[0]).toBe(false); }); it('should return true if featurePreviewEnabled is true and feature is in userPreferences', () => { - const { result } = renderHook( - () => { - return useFeaturePreview('quickReactions'); - }, - { - wrapper: ({ children }) => ( - <MockedSettingsContext - settings={{ - Accounts_AllowFeaturePreview: true, + const { result } = renderHook(() => useFeaturePreview('quickReactions'), { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: [ + { + name: 'quickReactions', + value: true, + }, + ], }} > - <MockedUserContext - userPreferences={{ - featuresPreview: [ - { - name: 'quickReactions', - value: true, - }, - ], - }} - > - {children} - </MockedUserContext> - </MockedSettingsContext> - ), - }, - ); + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }); expect(result.all[0]).toBe(true); }); -const createUserContextValue = ({ userPreferences }: { userPreferences?: Record<string, unknown> }): ContextType<typeof UserContext> => { - return { - ...userContextValue, - ...(userPreferences && { queryPreference: (id) => [() => () => undefined, () => userPreferences[id as unknown as string] as any] }), - }; -}; +const createUserContextValue = ({ userPreferences }: { userPreferences?: Record<string, unknown> }): ContextType<typeof UserContext> => ({ + ...userContextValue, + ...(userPreferences && { queryPreference: (id) => [() => () => undefined, () => userPreferences[id as unknown as string] as any] }), +}); const createSettingContextValue = ({ settings }: { settings?: Record<string, ISetting['value']> }): ContextType<typeof SettingsContext> => { const cache = new Map<string, ISetting['value']>(); @@ -162,9 +145,7 @@ export const MockedSettingsContext = ({ }: { children: React.ReactNode; settings?: Record<string, ISetting['value']>; -}) => { - return <SettingsContext.Provider value={createSettingContextValue({ settings })}>{children}</SettingsContext.Provider>; -}; +}) => <SettingsContext.Provider value={createSettingContextValue({ settings })}>{children}</SettingsContext.Provider>; export const MockedUserContext = ({ userPreferences, @@ -172,6 +153,4 @@ export const MockedUserContext = ({ }: { children: React.ReactNode; userPreferences?: Record<string, unknown>; -}) => { - return <UserContext.Provider value={createUserContextValue({ userPreferences })}>{children}</UserContext.Provider>; -}; +}) => <UserContext.Provider value={createUserContextValue({ userPreferences })}>{children}</UserContext.Provider>; diff --git a/apps/meteor/client/hooks/useFeaturePreview.ts b/packages/ui-client/src/hooks/useFeaturePreview.ts similarity index 100% rename from apps/meteor/client/hooks/useFeaturePreview.ts rename to packages/ui-client/src/hooks/useFeaturePreview.ts diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx new file mode 100644 index 000000000000..5e1aacd7197b --- /dev/null +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx @@ -0,0 +1,73 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { MockedSettingsContext, MockedUserContext } from './useFeaturePreview.spec'; +import { useFeaturePreviewList, defaultFeaturesPreview } from './useFeaturePreviewList'; + +it('should return the number of unseen features and Accounts_AllowFeaturePreview enabled ', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> + </MockedSettingsContext> + ), + }); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: defaultFeaturesPreview.length, + }), + ); +}); + +it('should return the number of unseen features and Accounts_AllowFeaturePreview disabled ', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: false, + }} + > + <MockedUserContext userPreferences={{}}>{children}</MockedUserContext> + </MockedSettingsContext> + ), + }); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: false, + unseenFeatures: 0, + }), + ); +}); + +it('should return 0 unseen features', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: defaultFeaturesPreview, + }} + > + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }); + + expect(result.all[0]).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: 0, + }), + ); +}); diff --git a/apps/meteor/client/hooks/useFeaturePreviewList.ts b/packages/ui-client/src/hooks/useFeaturePreviewList.ts similarity index 100% rename from apps/meteor/client/hooks/useFeaturePreviewList.ts rename to packages/ui-client/src/hooks/useFeaturePreviewList.ts diff --git a/packages/ui-client/src/index.ts b/packages/ui-client/src/index.ts index 07635cbbc8e7..0e4454d38dbf 100644 --- a/packages/ui-client/src/index.ts +++ b/packages/ui-client/src/index.ts @@ -1 +1,3 @@ export * from './components'; +export * from './hooks/useFeaturePreview'; +export * from './hooks/useFeaturePreviewList'; diff --git a/packages/ui-client/tsconfig.json b/packages/ui-client/tsconfig.json index c7077d14134e..e2be47cf5499 100644 --- a/packages/ui-client/tsconfig.json +++ b/packages/ui-client/tsconfig.json @@ -2,8 +2,7 @@ "extends": "../../tsconfig.base.client.json", "compilerOptions": { "rootDir": "./src", - "outDir": "./dist", + "outDir": "./dist" }, - "include": ["./src/**/*"], - "exclude": ["./dist/**/*"], + "include": ["./src/**/*"] } diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 74790595a41c..659b2b7af448 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -15,12 +15,12 @@ "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", "@types/babel__core": "~7.20.1", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", - "jest": "~29.5.0", + "jest": "~29.6.1", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 11c7a722d974..94a12d58abd6 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -7,13 +7,13 @@ "@rocket.chat/emitter": "next", "@rocket.chat/fuselage-hooks": "next", "@rocket.chat/rest-typings": "workspace:^", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", "@types/use-sync-external-store": "^0.0.3", "eslint": "~8.43.0", "eslint-plugin-react-hooks": "^4.6.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "mongodb": "^4.12.1", "react": "~17.0.2", "ts-jest": "~29.0.5", diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 0df5acf7d269..e60ad8140d77 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -19,12 +19,12 @@ "@storybook/react": "~6.5.16", "@storybook/testing-library": "~0.0.13", "@types/babel__core": "~7.20.1", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", - "jest": "~29.5.0", + "jest": "~29.6.1", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index 39933f5e16ed..e0dd396776e0 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -36,9 +36,9 @@ "use-subscription": "^1.8.0" }, "devDependencies": { - "@types/react": "^17.0.62", + "@types/react": "~17.0.62", "@types/react-beautiful-dnd": "^13.1.4", - "@types/react-dom": "^17.0.20", + "@types/react-dom": "~17.0.20", "@types/use-subscription": "^1.0.0", "@typescript-eslint/eslint-plugin": "~5.60.0", "@typescript-eslint/parser": "~5.60.0", diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index 2eadd13319c3..d5b37ffba90c 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -8,9 +8,9 @@ "@rocket.chat/ui-contexts": "workspace:^", "@tanstack/react-query": "^4.16.1", "@testing-library/react": "^13.3.0", - "@types/jest": "~29.5.2", + "@types/jest": "~29.5.3", "eslint": "~8.43.0", - "jest": "~29.5.0", + "jest": "~29.6.1", "react-hook-form": "^7.34.2", "ts-jest": "~29.0.5", "typescript": "~5.1.3" diff --git a/yarn.lock b/yarn.lock index 364eca70ff70..4ba47faae660 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3938,7 +3938,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.19.0, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4, @babel/traverse@npm:^7.7.2": +"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.19.0, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4": version: 7.21.4 resolution: "@babel/traverse@npm:7.21.4" dependencies: @@ -5319,20 +5319,6 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/console@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - slash: ^3.0.0 - checksum: 9f4f4b8fabd1221361b7f2e92d4a90f5f8c2e2b29077249996ab3c8b7f765175ffee795368f8d6b5b2bb3adb32dc09319f7270c7c787b0d259e624e00e0f64a5 - languageName: node - linkType: hard - "@jest/console@npm:^29.6.1": version: 29.6.1 resolution: "@jest/console@npm:29.6.1" @@ -5347,47 +5333,6 @@ __metadata: languageName: node linkType: hard -"@jest/core@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/core@npm:29.5.0" - dependencies: - "@jest/console": ^29.5.0 - "@jest/reporters": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - ansi-escapes: ^4.2.1 - chalk: ^4.0.0 - ci-info: ^3.2.0 - exit: ^0.1.2 - graceful-fs: ^4.2.9 - jest-changed-files: ^29.5.0 - jest-config: ^29.5.0 - jest-haste-map: ^29.5.0 - jest-message-util: ^29.5.0 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.5.0 - jest-resolve-dependencies: ^29.5.0 - jest-runner: ^29.5.0 - jest-runtime: ^29.5.0 - jest-snapshot: ^29.5.0 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - jest-watcher: ^29.5.0 - micromatch: ^4.0.4 - pretty-format: ^29.5.0 - slash: ^3.0.0 - strip-ansi: ^6.0.0 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 9e8f5243fe82d5a57f3971e1b96f320058df7c315328a3a827263f3b17f64be10c80f4a9c1b1773628b64d2de6d607c70b5b2d5bf13e7f5ad04223e9ef6aac06 - languageName: node - linkType: hard - "@jest/core@npm:^29.6.1": version: 29.6.1 resolution: "@jest/core@npm:29.6.1" @@ -5438,18 +5383,6 @@ __metadata: languageName: node linkType: hard -"@jest/environment@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/environment@npm:29.5.0" - dependencies: - "@jest/fake-timers": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - jest-mock: ^29.5.0 - checksum: 921de6325cd4817dec6685e5ff299b499b6379f3f9cf489b4b13588ee1f3820a0c77b49e6a087996b6de8f629f6f5251e636cba08d1bdb97d8071cc7d033c88a - languageName: node - linkType: hard - "@jest/environment@npm:^29.6.1": version: 29.6.1 resolution: "@jest/environment@npm:29.6.1" @@ -5480,16 +5413,6 @@ __metadata: languageName: node linkType: hard -"@jest/expect@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/expect@npm:29.5.0" - dependencies: - expect: ^29.5.0 - jest-snapshot: ^29.5.0 - checksum: bd10e295111547e6339137107d83986ab48d46561525393834d7d2d8b2ae9d5626653f3f5e48e5c3fa742ac982e97bdf1f541b53b9e1d117a247b08e938527f6 - languageName: node - linkType: hard - "@jest/expect@npm:^29.6.1": version: 29.6.1 resolution: "@jest/expect@npm:29.6.1" @@ -5500,20 +5423,6 @@ __metadata: languageName: node linkType: hard -"@jest/fake-timers@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/fake-timers@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@sinonjs/fake-timers": ^10.0.2 - "@types/node": "*" - jest-message-util: ^29.5.0 - jest-mock: ^29.5.0 - jest-util: ^29.5.0 - checksum: 69930c6922341f244151ec0d27640852ec96237f730fc024da1f53143d31b43cde75d92f9d8e5937981cdce3b31416abc3a7090a0d22c2377512c4a6613244ee - languageName: node - linkType: hard - "@jest/fake-timers@npm:^29.6.1": version: 29.6.1 resolution: "@jest/fake-timers@npm:29.6.1" @@ -5528,18 +5437,6 @@ __metadata: languageName: node linkType: hard -"@jest/globals@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/globals@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/expect": ^29.5.0 - "@jest/types": ^29.5.0 - jest-mock: ^29.5.0 - checksum: b309ab8f21b571a7c672608682e84bbdd3d2b554ddf81e4e32617fec0a69094a290ab42e3c8b2c66ba891882bfb1b8b2736720ea1285b3ad646d55c2abefedd9 - languageName: node - linkType: hard - "@jest/globals@npm:^29.6.1": version: 29.6.1 resolution: "@jest/globals@npm:29.6.1" @@ -5552,43 +5449,6 @@ __metadata: languageName: node linkType: hard -"@jest/reporters@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/reporters@npm:29.5.0" - dependencies: - "@bcoe/v8-coverage": ^0.2.3 - "@jest/console": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@jridgewell/trace-mapping": ^0.3.15 - "@types/node": "*" - chalk: ^4.0.0 - collect-v8-coverage: ^1.0.0 - exit: ^0.1.2 - glob: ^7.1.3 - graceful-fs: ^4.2.9 - istanbul-lib-coverage: ^3.0.0 - istanbul-lib-instrument: ^5.1.0 - istanbul-lib-report: ^3.0.0 - istanbul-lib-source-maps: ^4.0.0 - istanbul-reports: ^3.1.3 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - jest-worker: ^29.5.0 - slash: ^3.0.0 - string-length: ^4.0.1 - strip-ansi: ^6.0.0 - v8-to-istanbul: ^9.0.1 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 481268aac9a4a75cc49c4df1273d6b111808dec815e9d009dad717c32383ebb0cebac76e820ad1ab44e207540e1c2fe1e640d44c4f262de92ab1933e057fdeeb - languageName: node - linkType: hard - "@jest/reporters@npm:^29.6.1": version: 29.6.1 resolution: "@jest/reporters@npm:29.6.1" @@ -5653,17 +5513,6 @@ __metadata: languageName: node linkType: hard -"@jest/source-map@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/source-map@npm:29.4.3" - dependencies: - "@jridgewell/trace-mapping": ^0.3.15 - callsites: ^3.0.0 - graceful-fs: ^4.2.9 - checksum: 2301d225145f8123540c0be073f35a80fd26a2f5e59550fd68525d8cea580fb896d12bf65106591ffb7366a8a19790076dbebc70e0f5e6ceb51f81827ed1f89c - languageName: node - linkType: hard - "@jest/source-map@npm:^29.6.0": version: 29.6.0 resolution: "@jest/source-map@npm:29.6.0" @@ -5675,18 +5524,6 @@ __metadata: languageName: node linkType: hard -"@jest/test-result@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/test-result@npm:29.5.0" - dependencies: - "@jest/console": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/istanbul-lib-coverage": ^2.0.0 - collect-v8-coverage: ^1.0.0 - checksum: 2e8ff5242227ab960c520c3ea0f6544c595cc1c42fa3873c158e9f4f685f4ec9670ec08a4af94ae3885c0005a43550a9595191ffbc27a0965df27d9d98bbf901 - languageName: node - linkType: hard - "@jest/test-result@npm:^29.6.1": version: 29.6.1 resolution: "@jest/test-result@npm:29.6.1" @@ -5699,18 +5536,6 @@ __metadata: languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/test-sequencer@npm:29.5.0" - dependencies: - "@jest/test-result": ^29.5.0 - graceful-fs: ^4.2.9 - jest-haste-map: ^29.5.0 - slash: ^3.0.0 - checksum: eca34b4aeb2fda6dfb7f9f4b064c858a7adf64ec5c6091b6f4ed9d3c19549177cbadcf1c615c4c182688fa1cf085c8c55c3ca6eea40719a34554b0bf071d842e - languageName: node - linkType: hard - "@jest/test-sequencer@npm:^29.6.1": version: 29.6.1 resolution: "@jest/test-sequencer@npm:29.6.1" @@ -9426,9 +9251,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/account-utils@workspace:packages/account-utils" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -9439,13 +9264,13 @@ __metadata: resolution: "@rocket.chat/agenda@workspace:packages/agenda" dependencies: "@types/debug": ^4.1.8 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 cron: ~1.8.2 date.js: ~0.3.3 debug: ~4.1.1 eslint: ~8.43.0 human-interval: ^2.0.1 - jest: ~29.5.0 + jest: ~29.6.1 moment-timezone: ~0.5.43 mongodb: ^4.12.1 ts-jest: ~29.0.5 @@ -9461,11 +9286,11 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@swc/core": ^1.3.66 "@swc/jest": ^0.2.26 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/strict-uri-encode": ^2.0.0 eslint: ~8.43.0 filter-obj: ^3.0.0 - jest: ~29.5.0 + jest: ~29.6.1 jest-fetch-mock: ^3.0.3 query-string: ^7.1.3 split-on-first: ^3.0.0 @@ -9551,7 +9376,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -9561,10 +9386,10 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/cas-validate@workspace:packages/cas-validate" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 cheerio: 1.0.0-rc.10 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -9588,11 +9413,11 @@ __metadata: "@types/babel__core": ^7 "@types/babel__preset-env": ^7 "@types/fibers": ^3.1.1 - "@types/jest": ^29.5.1 + "@types/jest": ~29.5.3 babel-jest: ^29.5.0 eslint: ~8.43.0 fibers: ^5.0.3 - jest: ^29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 prettier: ~2.8.8 typescript: ~5.1.3 @@ -9623,9 +9448,9 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/random": "workspace:^" - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 eslint: ^8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -9675,11 +9500,11 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@swc/core": ^1.3.66 "@swc/jest": ^0.2.26 - "@types/jest": ^29.5.2 + "@types/jest": ~29.5.3 "@types/ws": ^8.5.5 eslint: ^8.43.0 - jest: ^29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 jest-websocket-mock: ^2.4.0 typescript: ~5.1.3 ws: ^8.13.0 @@ -9979,7 +9804,7 @@ __metadata: "@swc/jest": ^0.2.26 "@testing-library/jest-dom": ^5.16.5 "@testing-library/react": ~12.1.5 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/katex": ~0.16.0 "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 @@ -9994,8 +9819,8 @@ __metadata: eslint-plugin-storybook: ~0.6.12 highlight.js: ^11.5.1 identity-obj-proxy: ^3.0.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 katex: ~0.16.7 outdent: ^0.8.0 react-docgen-typescript-plugin: ~1.0.5 @@ -10026,10 +9851,10 @@ __metadata: "@babel/preset-typescript": ~7.22.5 "@types/babel__core": ~7.20.1 "@types/babel__preset-env": ~7.9.2 - "@types/jest": ^29.5.2 + "@types/jest": ~29.5.3 babel-jest: ^29.5.0 eslint: ^8.43.0 - jest: ^29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 tsup: ^6.7.0 typescript: ~5.1.3 @@ -10154,11 +9979,11 @@ __metadata: dependencies: "@types/chalk": ^2.2.0 "@types/ejson": ^2.2.0 - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 chalk: ^4.0.0 ejson: ^2.2.3 eslint: ^8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10256,6 +10081,7 @@ __metadata: "@rocket.chat/logo": next "@rocket.chat/memo": next "@rocket.chat/message-parser": next + "@rocket.chat/mock-providers": "workspace:^" "@rocket.chat/model-typings": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/mp3-encoder": 0.24.0 @@ -10454,7 +10280,7 @@ __metadata: imap: ^0.8.19 ip-range-check: ^0.2.0 is-svg: ^4.3.2 - jest: ^29.6.1 + jest: ~29.6.1 jquery: ^3.6.0 jschardet: ^3.0.0 jsdom: ^16.7.0 @@ -10569,15 +10395,15 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/mock-providers@workspace:packages/mock-providers": +"@rocket.chat/mock-providers@workspace:^, @rocket.chat/mock-providers@workspace:packages/mock-providers": version: 0.0.0-use.local resolution: "@rocket.chat/mock-providers@workspace:packages/mock-providers" dependencies: "@rocket.chat/ui-contexts": "workspace:*" "@tanstack/react-query": ^4.16.1 - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 eslint: ^8.12.0 - jest: ~29.5.0 + jest: ~29.6.1 react: ~17.0.2 ts-jest: ~29.0.5 typescript: ~5.0.2 @@ -10592,10 +10418,10 @@ __metadata: resolution: "@rocket.chat/model-typings@workspace:packages/model-typings" dependencies: "@rocket.chat/core-typings": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/node-rsa": ^1.1.1 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -10607,9 +10433,9 @@ __metadata: resolution: "@rocket.chat/models@workspace:packages/models" dependencies: "@rocket.chat/model-typings": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10638,14 +10464,14 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": next "@rocket.chat/tools": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/node": ^14.18.51 ejson: ^2.2.3 emoji-toolkit: ^7.0.1 eslint: ~8.43.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 - jest: ~29.5.0 + jest: ~29.6.1 mem: ^8.1.1 moment-timezone: ^0.5.43 mongo-message-queue: ^1.0.0 @@ -10725,15 +10551,15 @@ __metadata: "@testing-library/jest-dom": ^5.16.5 "@testing-library/react": ~13.4.0 "@types/emojione": ^2.2.6 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 - "@types/react-dom": ^18.2.5 + "@types/react-dom": ~17.0.20 "@types/testing-library__jest-dom": ~5.14.6 emoji-assets: ^7.0.1 emoji-toolkit: ^7.0.1 eslint: ~8.43.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 moment: ^2.29.4 moment-timezone: ^0.5.43 react: ^18.2.0 @@ -10747,9 +10573,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/poplib@workspace:packages/node-poplib" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10801,7 +10627,7 @@ __metadata: "@types/node": ^14.18.51 babel-jest: ^29.0.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 typescript: ~5.1.3 languageName: unknown @@ -10859,8 +10685,8 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 eslint: ~8.43.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -10896,12 +10722,12 @@ __metadata: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/message-parser": next "@rocket.chat/ui-kit": next - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 ajv: ^8.11.0 ajv-formats: ^2.1.1 eslint: ~8.43.0 - jest: ~29.5.0 - jest-environment-jsdom: ~29.5.0 + jest: ~29.6.1 + jest-environment-jsdom: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -10925,12 +10751,12 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/server-fetch@workspace:packages/server-fetch" dependencies: - "@types/jest": ^27.4.1 + "@types/jest": ~29.5.3 "@types/proxy-from-env": ^1.0.1 eslint: ^8.43.0 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.1 - jest: ~29.5.0 + jest: ~29.6.1 node-fetch: 2.3.0 proxy-from-env: ^1.1.0 ts-jest: ~29.0.5 @@ -10948,7 +10774,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 ts-jest: ~29.0.5 typescript: ~5.1.3 languageName: unknown @@ -11025,9 +10851,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/tools@workspace:packages/tools" dependencies: - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 moment-timezone: ^0.5.43 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11043,6 +10869,7 @@ __metadata: "@rocket.chat/fuselage": next "@rocket.chat/fuselage-hooks": next "@rocket.chat/icons": next + "@rocket.chat/mock-providers": "workspace:^" "@rocket.chat/ui-contexts": "workspace:~" "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 @@ -11054,8 +10881,11 @@ __metadata: "@storybook/manager-webpack4": ~6.5.16 "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 + "@swc/jest": ^0.2.26 + "@testing-library/react": ^12.1.2 + "@testing-library/react-hooks": ^8.0.1 "@types/babel__core": ~7.20.1 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 eslint: ~8.43.0 @@ -11064,8 +10894,9 @@ __metadata: eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 eslint-plugin-testing-library: ~5.11.0 - jest: ~29.5.0 - react: ~17.0.2 + jest: ~29.6.1 + react: ^17.0.2 + react-dom: ^17.0.2 react-hook-form: ^7.30.0 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11095,12 +10926,12 @@ __metadata: "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 "@types/babel__core": ~7.20.1 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 - jest: ~29.5.0 + jest: ~29.6.1 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11120,13 +10951,13 @@ __metadata: "@rocket.chat/emitter": next "@rocket.chat/fuselage-hooks": next "@rocket.chat/rest-typings": "workspace:^" - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 "@types/use-sync-external-store": ^0.0.3 eslint: ~8.43.0 eslint-plugin-react-hooks: ^4.6.0 - jest: ~29.5.0 + jest: ~29.6.1 mongodb: ^4.12.1 react: ~17.0.2 ts-jest: ~29.0.5 @@ -11171,14 +11002,14 @@ __metadata: "@storybook/manager-webpack4": ~6.5.16 "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 "@types/react": ~17.0.62 eslint: ~8.43.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-testing-library: ^5.11.0 - jest: ~29.5.0 + jest: ~29.6.1 react: ~17.0.2 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 @@ -11213,12 +11044,12 @@ __metadata: "@storybook/react": ~6.5.16 "@storybook/testing-library": ~0.0.13 "@types/babel__core": ~7.20.1 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 - jest: ~29.5.0 + jest: ~29.6.1 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -11252,9 +11083,9 @@ __metadata: "@rocket.chat/logo": next "@rocket.chat/styled": next "@rocket.chat/ui-contexts": "workspace:~" - "@types/react": ^17.0.62 + "@types/react": ~17.0.62 "@types/react-beautiful-dnd": ^13.1.4 - "@types/react-dom": ^17.0.20 + "@types/react-dom": ~17.0.20 "@types/use-subscription": ^1.0.0 "@typescript-eslint/eslint-plugin": ~5.60.0 "@typescript-eslint/parser": ~5.60.0 @@ -11286,9 +11117,9 @@ __metadata: "@rocket.chat/ui-contexts": "workspace:^" "@tanstack/react-query": ^4.16.1 "@testing-library/react": ^13.3.0 - "@types/jest": ~29.5.2 + "@types/jest": ~29.5.3 eslint: ~8.43.0 - jest: ~29.5.0 + jest: ~29.6.1 react-hook-form: ^7.34.2 ts-jest: ~29.0.5 typescript: ~5.1.3 @@ -13024,6 +12855,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^12.1.2, @testing-library/react@npm:~12.1.5": + version: 12.1.5 + resolution: "@testing-library/react@npm:12.1.5" + dependencies: + "@babel/runtime": ^7.12.5 + "@testing-library/dom": ^8.0.0 + "@types/react-dom": <18.0.0 + peerDependencies: + react: <18.0.0 + react-dom: <18.0.0 + checksum: 4abd0490405e709a7df584a0db604e508a4612398bb1326e8fa32dd9393b15badc826dcf6d2f7525437886d507871f719f127b9860ed69ddd204d1fa834f576a + languageName: node + linkType: hard + "@testing-library/react@npm:^13.3.0, @testing-library/react@npm:~13.4.0": version: 13.4.0 resolution: "@testing-library/react@npm:13.4.0" @@ -13038,20 +12883,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/react@npm:~12.1.5": - version: 12.1.5 - resolution: "@testing-library/react@npm:12.1.5" - dependencies: - "@babel/runtime": ^7.12.5 - "@testing-library/dom": ^8.0.0 - "@types/react-dom": <18.0.0 - peerDependencies: - react: <18.0.0 - react-dom: <18.0.0 - checksum: 4abd0490405e709a7df584a0db604e508a4612398bb1326e8fa32dd9393b15badc826dcf6d2f7525437886d507871f719f127b9860ed69ddd204d1fa834f576a - languageName: node - linkType: hard - "@testing-library/user-event@npm:^13.2.1, @testing-library/user-event@npm:~13.5.0": version: 13.5.0 resolution: "@testing-library/user-event@npm:13.5.0" @@ -13714,23 +13545,13 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:^27.4.1": - version: 27.5.2 - resolution: "@types/jest@npm:27.5.2" - dependencies: - jest-matcher-utils: ^27.0.0 - pretty-format: ^27.0.0 - checksum: 7e11c6826aa429ad990dc262e4e4b54aa36573287fddf15773e4137f07d11d3105f0dd9f1baff73252160a057df23f5529bb83b1bf83cd3f45f9460a5ca5c22e - languageName: node - linkType: hard - -"@types/jest@npm:^29.5.1, @types/jest@npm:^29.5.2, @types/jest@npm:~29.5.2": - version: 29.5.2 - resolution: "@types/jest@npm:29.5.2" +"@types/jest@npm:~29.5.3": + version: 29.5.3 + resolution: "@types/jest@npm:29.5.3" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: 7d205599ea3cccc262bad5cc173d3242d6bf8138c99458509230e4ecef07a52d6ddcde5a1dbd49ace655c0af51d2dbadef3748697292ea4d86da19d9e03e19c0 + checksum: e36bb92e0b9e5ea7d6f8832baa42f087fc1697f6cd30ec309a07ea4c268e06ec460f1f0cfd2581daf5eff5763475190ec1ad8ac6520c49ccfe4f5c0a48bfa676 languageName: node linkType: hard @@ -14254,15 +14075,6 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^17.0.20, @types/react-dom@npm:~17.0.20": - version: 17.0.20 - resolution: "@types/react-dom@npm:17.0.20" - dependencies: - "@types/react": ^17 - checksum: 525439fb14a033fc5dbe74711ecc50ec82273a528df9656594066a6219401e975101dafffd15d9a1a57a9442d52ea0c92eaacae09554dde27cd792e773f67467 - languageName: node - linkType: hard - "@types/react-dom@npm:^18.0.0": version: 18.0.10 resolution: "@types/react-dom@npm:18.0.10" @@ -14272,12 +14084,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.2.5": - version: 18.2.5 - resolution: "@types/react-dom@npm:18.2.5" +"@types/react-dom@npm:~17.0.20": + version: 17.0.20 + resolution: "@types/react-dom@npm:17.0.20" dependencies: - "@types/react": "*" - checksum: c48209f8c60cb9054f3deee5365bc9fd6dadd8f901b67f1612a334057b2671518fc5145f14aca63ff276a926ccb5358308a6cf58ec700178f382bb3ebde96d91 + "@types/react": ^17 + checksum: 525439fb14a033fc5dbe74711ecc50ec82273a528df9656594066a6219401e975101dafffd15d9a1a57a9442d52ea0c92eaacae09554dde27cd792e773f67467 languageName: node linkType: hard @@ -14304,7 +14116,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^17.0.62, @types/react@npm:~17.0.62": +"@types/react@npm:~17.0.62": version: 17.0.62 resolution: "@types/react@npm:17.0.62" dependencies: @@ -20980,13 +20792,6 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^27.5.1": - version: 27.5.1 - resolution: "diff-sequences@npm:27.5.1" - checksum: a00db5554c9da7da225db2d2638d85f8e41124eccbd56cbaefb3b276dcbb1c1c2ad851c32defe2055a54a4806f030656cbf6638105fd6ce97bb87b90b32a33ca - languageName: node - linkType: hard - "diff-sequences@npm:^28.1.1": version: 28.1.1 resolution: "diff-sequences@npm:28.1.1" @@ -22899,7 +22704,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.5.0": +"expect@npm:^29.0.0": version: 29.5.0 resolution: "expect@npm:29.5.0" dependencies: @@ -27451,34 +27256,6 @@ __metadata: languageName: node linkType: hard -"jest-circus@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-circus@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/expect": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - co: ^4.6.0 - dedent: ^0.7.0 - is-generator-fn: ^2.0.0 - jest-each: ^29.5.0 - jest-matcher-utils: ^29.5.0 - jest-message-util: ^29.5.0 - jest-runtime: ^29.5.0 - jest-snapshot: ^29.5.0 - jest-util: ^29.5.0 - p-limit: ^3.1.0 - pretty-format: ^29.5.0 - pure-rand: ^6.0.0 - slash: ^3.0.0 - stack-utils: ^2.0.3 - checksum: 44ff5d06acedae6de6c866e20e3b61f83e29ab94cf9f960826e7e667de49c12dd9ab9dffd7fa3b7d1f9688a8b5bfb1ebebadbea69d9ed0d3f66af4a0ff8c2b27 - languageName: node - linkType: hard - "jest-circus@npm:^29.6.1": version: 29.6.1 resolution: "jest-circus@npm:29.6.1" @@ -27507,33 +27284,6 @@ __metadata: languageName: node linkType: hard -"jest-cli@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-cli@npm:29.5.0" - dependencies: - "@jest/core": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/types": ^29.5.0 - chalk: ^4.0.0 - exit: ^0.1.2 - graceful-fs: ^4.2.9 - import-local: ^3.0.2 - jest-config: ^29.5.0 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - prompts: ^2.0.1 - yargs: ^17.3.1 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: 39897bbbc0f0d8a6b975ab12fd13887eaa28d92e3dee9e0173a5cb913ae8cc2ae46e090d38c6d723e84d9d6724429cd08685b4e505fa447d31ca615630c7dbba - languageName: node - linkType: hard - "jest-cli@npm:^29.6.1": version: 29.6.1 resolution: "jest-cli@npm:29.6.1" @@ -27561,44 +27311,6 @@ __metadata: languageName: node linkType: hard -"jest-config@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-config@npm:29.5.0" - dependencies: - "@babel/core": ^7.11.6 - "@jest/test-sequencer": ^29.5.0 - "@jest/types": ^29.5.0 - babel-jest: ^29.5.0 - chalk: ^4.0.0 - ci-info: ^3.2.0 - deepmerge: ^4.2.2 - glob: ^7.1.3 - graceful-fs: ^4.2.9 - jest-circus: ^29.5.0 - jest-environment-node: ^29.5.0 - jest-get-type: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.5.0 - jest-runner: ^29.5.0 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - micromatch: ^4.0.4 - parse-json: ^5.2.0 - pretty-format: ^29.5.0 - slash: ^3.0.0 - strip-json-comments: ^3.1.1 - peerDependencies: - "@types/node": "*" - ts-node: ">=9.0.0" - peerDependenciesMeta: - "@types/node": - optional: true - ts-node: - optional: true - checksum: c37c4dab964c54ab293d4e302d40b09687037ac9d00b88348ec42366970747feeaf265e12e3750cd3660b40c518d4031335eda11ac10b70b10e60797ebbd4b9c - languageName: node - linkType: hard - "jest-config@npm:^29.6.1": version: 29.6.1 resolution: "jest-config@npm:29.6.1" @@ -27637,18 +27349,6 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^27.5.1": - version: 27.5.1 - resolution: "jest-diff@npm:27.5.1" - dependencies: - chalk: ^4.0.0 - diff-sequences: ^27.5.1 - jest-get-type: ^27.5.1 - pretty-format: ^27.5.1 - checksum: 8be27c1e1ee57b2bb2bef9c0b233c19621b4c43d53a3c26e2c00a4e805eb4ea11fe1694a06a9fb0e80ffdcfdc0d2b1cb0b85920b3f5c892327ecd1e7bd96b865 - languageName: node - linkType: hard - "jest-diff@npm:^28.0.2": version: 28.1.3 resolution: "jest-diff@npm:28.1.3" @@ -27694,19 +27394,6 @@ __metadata: languageName: node linkType: hard -"jest-each@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-each@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - chalk: ^4.0.0 - jest-get-type: ^29.4.3 - jest-util: ^29.5.0 - pretty-format: ^29.5.0 - checksum: b8b297534d25834c5d4e31e4c687359787b1e402519e42664eb704cc3a12a7a91a017565a75acb02e8cf9afd3f4eef3350bd785276bec0900184641b765ff7a5 - languageName: node - linkType: hard - "jest-each@npm:^29.6.1": version: 29.6.1 resolution: "jest-each@npm:29.6.1" @@ -27720,38 +27407,24 @@ __metadata: languageName: node linkType: hard -"jest-environment-jsdom@npm:~29.5.0": - version: 29.5.0 - resolution: "jest-environment-jsdom@npm:29.5.0" +"jest-environment-jsdom@npm:~29.6.1": + version: 29.6.1 + resolution: "jest-environment-jsdom@npm:29.6.1" dependencies: - "@jest/environment": ^29.5.0 - "@jest/fake-timers": ^29.5.0 - "@jest/types": ^29.5.0 + "@jest/environment": ^29.6.1 + "@jest/fake-timers": ^29.6.1 + "@jest/types": ^29.6.1 "@types/jsdom": ^20.0.0 "@types/node": "*" - jest-mock: ^29.5.0 - jest-util: ^29.5.0 + jest-mock: ^29.6.1 + jest-util: ^29.6.1 jsdom: ^20.0.0 peerDependencies: canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 3df7fc85275711f20b483ac8cd8c04500704ed0f69791eb55c574b38f5a39470f03d775cf20c1025bc1884916ac0573aa2fa4db1bb74225bc7fdd95ba97ad0da - languageName: node - linkType: hard - -"jest-environment-node@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-environment-node@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/fake-timers": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - jest-mock: ^29.5.0 - jest-util: ^29.5.0 - checksum: 57981911cc20a4219b0da9e22b2e3c9f31b505e43f78e61c899e3227ded455ce1a3a9483842c69cfa4532f02cfb536ae0995bf245f9211608edacfc1e478d411 + checksum: e8a9bff00a011235b004699f34bc85b18fdac82049513410cbf2dc1c2dd332bc1b4f108976412df1d29f2fa8bf0360aaf84eb0f5b4db1db2fb7fc7155dc14be7 languageName: node linkType: hard @@ -27779,13 +27452,6 @@ __metadata: languageName: node linkType: hard -"jest-get-type@npm:^27.5.1": - version: 27.5.1 - resolution: "jest-get-type@npm:27.5.1" - checksum: 63064ab70195c21007d897c1157bf88ff94a790824a10f8c890392e7d17eda9c3900513cb291ca1c8d5722cad79169764e9a1279f7c8a9c4cd6e9109ff04bbc0 - languageName: node - linkType: hard - "jest-get-type@npm:^28.0.2": version: 28.0.2 resolution: "jest-get-type@npm:28.0.2" @@ -27871,16 +27537,6 @@ __metadata: languageName: node linkType: hard -"jest-leak-detector@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-leak-detector@npm:29.5.0" - dependencies: - jest-get-type: ^29.4.3 - pretty-format: ^29.5.0 - checksum: 0fb845da7ac9cdfc9b3b2e35f6f623a41c547d7dc0103ceb0349013459d00de5870b5689a625e7e37f9644934b40e8f1dcdd5422d14d57470600350364676313 - languageName: node - linkType: hard - "jest-leak-detector@npm:^29.6.1": version: 29.6.1 resolution: "jest-leak-detector@npm:29.6.1" @@ -27891,18 +27547,6 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^27.0.0": - version: 27.5.1 - resolution: "jest-matcher-utils@npm:27.5.1" - dependencies: - chalk: ^4.0.0 - jest-diff: ^27.5.1 - jest-get-type: ^27.5.1 - pretty-format: ^27.5.1 - checksum: bb2135fc48889ff3fe73888f6cc7168ddab9de28b51b3148f820c89fdfd2effdcad005f18be67d0b9be80eda208ad47290f62f03d0a33f848db2dd0273c8217a - languageName: node - linkType: hard - "jest-matcher-utils@npm:^29.5.0": version: 29.5.0 resolution: "jest-matcher-utils@npm:29.5.0" @@ -27971,17 +27615,6 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-mock@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@types/node": "*" - jest-util: ^29.5.0 - checksum: 2a9cf07509948fa8608898c445f04fe4dd6e2049ff431e5531eee028c808d3ba3c67f226ac87b0cf383feaa1055776900d197c895e89783016886ac17a4ff10c - languageName: node - linkType: hard - "jest-mock@npm:^29.6.1": version: 29.6.1 resolution: "jest-mock@npm:29.6.1" @@ -28019,16 +27652,6 @@ __metadata: languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-resolve-dependencies@npm:29.5.0" - dependencies: - jest-regex-util: ^29.4.3 - jest-snapshot: ^29.5.0 - checksum: 479d2e5365d58fe23f2b87001e2e0adcbffe0147700e85abdec8f14b9703b0a55758c1929a9989e3f5d5e954fb88870ea4bfa04783523b664562fcf5f10b0edf - languageName: node - linkType: hard - "jest-resolve-dependencies@npm:^29.6.1": version: 29.6.1 resolution: "jest-resolve-dependencies@npm:29.6.1" @@ -28039,23 +27662,6 @@ __metadata: languageName: node linkType: hard -"jest-resolve@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-resolve@npm:29.5.0" - dependencies: - chalk: ^4.0.0 - graceful-fs: ^4.2.9 - jest-haste-map: ^29.5.0 - jest-pnp-resolver: ^1.2.2 - jest-util: ^29.5.0 - jest-validate: ^29.5.0 - resolve: ^1.20.0 - resolve.exports: ^2.0.0 - slash: ^3.0.0 - checksum: 9a125f3cf323ceef512089339d35f3ee37f79fe16a831fb6a26773ea6a229b9e490d108fec7af334142e91845b5996de8e7cdd85a4d8d617078737d804e29c8f - languageName: node - linkType: hard - "jest-resolve@npm:^29.6.1": version: 29.6.1 resolution: "jest-resolve@npm:29.6.1" @@ -28073,35 +27679,6 @@ __metadata: languageName: node linkType: hard -"jest-runner@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-runner@npm:29.5.0" - dependencies: - "@jest/console": ^29.5.0 - "@jest/environment": ^29.5.0 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - emittery: ^0.13.1 - graceful-fs: ^4.2.9 - jest-docblock: ^29.4.3 - jest-environment-node: ^29.5.0 - jest-haste-map: ^29.5.0 - jest-leak-detector: ^29.5.0 - jest-message-util: ^29.5.0 - jest-resolve: ^29.5.0 - jest-runtime: ^29.5.0 - jest-util: ^29.5.0 - jest-watcher: ^29.5.0 - jest-worker: ^29.5.0 - p-limit: ^3.1.0 - source-map-support: 0.5.13 - checksum: 437dea69c5dddca22032259787bac74790d5a171c9d804711415f31e5d1abfb64fa52f54a9015bb17a12b858fd0cf3f75ef6f3c9e94255a8596e179f707229c4 - languageName: node - linkType: hard - "jest-runner@npm:^29.6.1": version: 29.6.1 resolution: "jest-runner@npm:29.6.1" @@ -28131,36 +27708,6 @@ __metadata: languageName: node linkType: hard -"jest-runtime@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-runtime@npm:29.5.0" - dependencies: - "@jest/environment": ^29.5.0 - "@jest/fake-timers": ^29.5.0 - "@jest/globals": ^29.5.0 - "@jest/source-map": ^29.4.3 - "@jest/test-result": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - cjs-module-lexer: ^1.0.0 - collect-v8-coverage: ^1.0.0 - glob: ^7.1.3 - graceful-fs: ^4.2.9 - jest-haste-map: ^29.5.0 - jest-message-util: ^29.5.0 - jest-mock: ^29.5.0 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.5.0 - jest-snapshot: ^29.5.0 - jest-util: ^29.5.0 - slash: ^3.0.0 - strip-bom: ^4.0.0 - checksum: 7af27bd9d54cf1c5735404cf8d76c6509d5610b1ec0106a21baa815c1aff15d774ce534ac2834bc440dccfe6348bae1885fd9a806f23a94ddafdc0f5bae4b09d - languageName: node - linkType: hard - "jest-runtime@npm:^29.6.1": version: 29.6.1 resolution: "jest-runtime@npm:29.6.1" @@ -28201,37 +27748,6 @@ __metadata: languageName: node linkType: hard -"jest-snapshot@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-snapshot@npm:29.5.0" - dependencies: - "@babel/core": ^7.11.6 - "@babel/generator": ^7.7.2 - "@babel/plugin-syntax-jsx": ^7.7.2 - "@babel/plugin-syntax-typescript": ^7.7.2 - "@babel/traverse": ^7.7.2 - "@babel/types": ^7.3.3 - "@jest/expect-utils": ^29.5.0 - "@jest/transform": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/babel__traverse": ^7.0.6 - "@types/prettier": ^2.1.5 - babel-preset-current-node-syntax: ^1.0.0 - chalk: ^4.0.0 - expect: ^29.5.0 - graceful-fs: ^4.2.9 - jest-diff: ^29.5.0 - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.5.0 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - natural-compare: ^1.4.0 - pretty-format: ^29.5.0 - semver: ^7.3.5 - checksum: fe5df54122ed10eed625de6416a45bc4958d5062b018f05b152bf9785ab7f355dcd55e40cf5da63895bf8278f8d7b2bb4059b2cfbfdee18f509d455d37d8aa2b - languageName: node - linkType: hard - "jest-snapshot@npm:^29.6.1": version: 29.6.1 resolution: "jest-snapshot@npm:29.6.1" @@ -28303,20 +27819,6 @@ __metadata: languageName: node linkType: hard -"jest-validate@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-validate@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - camelcase: ^6.2.0 - chalk: ^4.0.0 - jest-get-type: ^29.4.3 - leven: ^3.1.0 - pretty-format: ^29.5.0 - checksum: 43ca5df7cb75572a254ac3e92fbbe7be6b6a1be898cc1e887a45d55ea003f7a112717d814a674d37f9f18f52d8de40873c8f084f17664ae562736c78dd44c6a1 - languageName: node - linkType: hard - "jest-validate@npm:^29.6.1": version: 29.6.1 resolution: "jest-validate@npm:29.6.1" @@ -28331,22 +27833,6 @@ __metadata: languageName: node linkType: hard -"jest-watcher@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-watcher@npm:29.5.0" - dependencies: - "@jest/test-result": ^29.5.0 - "@jest/types": ^29.5.0 - "@types/node": "*" - ansi-escapes: ^4.2.1 - chalk: ^4.0.0 - emittery: ^0.13.1 - jest-util: ^29.5.0 - string-length: ^4.0.1 - checksum: 62303ac7bdc7e61a8b4239a239d018f7527739da2b2be6a81a7be25b74ca769f1c43ee8558ce8e72bb857245c46d6e03af331227ffb00a57280abb2a928aa776 - languageName: node - linkType: hard - "jest-watcher@npm:^29.6.1": version: 29.6.1 resolution: "jest-watcher@npm:29.6.1" @@ -28419,26 +27905,7 @@ __metadata: languageName: node linkType: hard -"jest@npm:^29.5.0, jest@npm:~29.5.0": - version: 29.5.0 - resolution: "jest@npm:29.5.0" - dependencies: - "@jest/core": ^29.5.0 - "@jest/types": ^29.5.0 - import-local: ^3.0.2 - jest-cli: ^29.5.0 - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: a8ff2eb0f421623412236e23cbe67c638127fffde466cba9606bc0c0553b4c1e5cb116d7e0ef990b5d1712851652c8ee461373b578df50857fe635b94ff455d5 - languageName: node - linkType: hard - -"jest@npm:^29.6.1": +"jest@npm:~29.6.1": version: 29.6.1 resolution: "jest@npm:29.6.1" dependencies: @@ -34514,7 +33981,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^27.0.0, pretty-format@npm:^27.0.2, pretty-format@npm:^27.5.1": +"pretty-format@npm:^27.0.2": version: 27.5.1 resolution: "pretty-format@npm:27.5.1" dependencies: From 54579fb41f9e29b26219172206ed26f9dfb7919e Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Tue, 11 Jul 2023 14:17:13 -0300 Subject: [PATCH 108/149] chore: stop importing action manager as global (#29766) --- .../ui-message/client/ActionButtonSyncer.ts | 56 ----- .../app/ui-message/client/ActionManager.js | 21 +- .../client/actionButtons/dropdownAction.ts | 28 --- .../client/actionButtons/messageAction.ts | 33 --- .../client/actionButtons/messageBox.ts | 42 ---- .../ui-message/client/actionButtons/tabbar.ts | 33 --- .../app/ui-utils/client/lib/MessageAction.ts | 2 +- .../app/ui-utils/client/lib/messageBox.ts | 2 +- .../UIKit/hooks/useUIKitHandleAction.tsx | 11 +- .../UIKit/hooks/useUIKitHandleClose.tsx | 26 +-- .../UIKit/hooks/useUIKitStateManager.tsx | 7 +- .../components/message/toolbox/Toolbox.tsx | 43 ++-- .../client/hooks/useAppActionButtons.ts | 201 ++++++++++++++++++ .../client/hooks/useAppSlashCommands.ts | 45 ++++ .../client/hooks/useAppUiKitInteraction.ts | 27 +++ .../client/hooks/useUiKitActionManager.ts | 11 + .../providers/ActionManagerProvider.tsx | 7 + .../actions/hooks/useAdministrationMenu.tsx | 6 +- .../header/actions/hooks/useAppsItems.tsx | 45 ++-- .../components/MarketplaceRequestBadge.tsx | 4 +- .../marketplace/hooks/useAppRequestStats.ts | 2 +- .../client/views/modal/uikit/UiKitModal.tsx | 13 +- .../uikit/hooks/useActionManagerState.ts | 7 +- .../ActionsToolbarDropdown.tsx | 12 +- .../room/contextualBar/Apps/AppsWithData.tsx | 17 +- .../views/room/hooks/useAppsContextualBar.ts | 7 +- .../views/room/providers/ToolboxProvider.tsx | 5 +- .../client/apps/communication/websockets.js | 3 +- ee/packages/ddp-client/src/types/streams.ts | 3 +- 29 files changed, 407 insertions(+), 312 deletions(-) delete mode 100644 apps/meteor/app/ui-message/client/ActionButtonSyncer.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/messageAction.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/messageBox.ts delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/tabbar.ts create mode 100644 apps/meteor/client/hooks/useAppActionButtons.ts create mode 100644 apps/meteor/client/hooks/useAppSlashCommands.ts create mode 100644 apps/meteor/client/hooks/useAppUiKitInteraction.ts create mode 100644 apps/meteor/client/hooks/useUiKitActionManager.ts diff --git a/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts b/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts deleted file mode 100644 index b737c112ec93..000000000000 --- a/apps/meteor/app/ui-message/client/ActionButtonSyncer.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import { UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui'; - -import * as TabBar from './actionButtons/tabbar'; -import * as MessageAction from './actionButtons/messageAction'; -import * as MessageBox from './actionButtons/messageBox'; -import * as DropdownAction from './actionButtons/dropdownAction'; -import { sdk } from '../../utils/client/lib/SDKClient'; - -let registeredButtons: Array<IUIActionButton> = []; - -const addButton = async (button: IUIActionButton): Promise<void> => { - switch (button.context) { - case UIActionButtonContext.MESSAGE_ACTION: - MessageAction.onAdded(button); - break; - case UIActionButtonContext.ROOM_ACTION: - TabBar.onAdded(button); - break; - case UIActionButtonContext.MESSAGE_BOX_ACTION: - MessageBox.onAdded(button); - break; - case UIActionButtonContext.USER_DROPDOWN_ACTION: - await DropdownAction.onAdded(button); - break; - } - - registeredButtons.push(Object.freeze(button)); -}; - -const removeButton = async (button: IUIActionButton): Promise<void> => { - switch (button.context) { - case UIActionButtonContext.MESSAGE_ACTION: - MessageAction.onRemoved(button); - break; - case UIActionButtonContext.ROOM_ACTION: - TabBar.onRemoved(button); - break; - case UIActionButtonContext.MESSAGE_BOX_ACTION: - MessageBox.onRemoved(button); - break; - case UIActionButtonContext.USER_DROPDOWN_ACTION: - await DropdownAction.onRemoved(button); - break; - } -}; - -export const loadButtons = (): Promise<void> => - sdk.rest.get('/apps/actionButtons').then((value) => { - registeredButtons.forEach((button) => removeButton(button)); - registeredButtons = []; - value.map(addButton); - }); - -Meteor.startup(() => loadButtons()); diff --git a/apps/meteor/app/ui-message/client/ActionManager.js b/apps/meteor/app/ui-message/client/ActionManager.js index 48165b99f436..78fd90e52e9b 100644 --- a/apps/meteor/app/ui-message/client/ActionManager.js +++ b/apps/meteor/app/ui-message/client/ActionManager.js @@ -1,18 +1,17 @@ import { UIKitIncomingInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; -import { Meteor } from 'meteor/meteor'; import { Random } from '@rocket.chat/random'; import { Emitter } from '@rocket.chat/emitter'; import { UIKitInteractionTypes } from '@rocket.chat/core-typings'; +import { lazy } from 'react'; -import Notifications from '../../notifications/client/lib/Notifications'; -import { CachedCollectionManager } from '../../ui-cached-collection/client'; -import { t } from '../../utils/client'; +import { t } from '../../utils/lib/i18n'; import * as banners from '../../../client/lib/banners'; import { dispatchToastMessage } from '../../../client/lib/toast'; -import { imperativeModal } from '../../../client/lib/imperativeModal'; -import UiKitModal from '../../../client/views/modal/uikit/UiKitModal'; import { sdk } from '../../utils/client/lib/SDKClient'; import { router } from '../../../client/providers/RouterProvider'; +import { imperativeModal } from '../../../client/lib/imperativeModal'; + +const UiKitModal = lazy(() => import('../../../client/views/modal/uikit/UiKitModal')); const events = new Emitter(); @@ -45,7 +44,7 @@ export const generateTriggerId = (appId) => { return triggerId; }; -const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) => { +export const handlePayloadUserInteraction = (type, { /* appId,*/ triggerId, ...data }) => { if (!triggersId.has(triggerId)) { return; } @@ -256,11 +255,3 @@ export const getUserInteractionPayloadByViewId = (viewId) => { return instance.payload; }; - -Meteor.startup(() => - CachedCollectionManager.onLogin(() => - Notifications.onUser('uiInteraction', ({ type, ...data }) => { - handlePayloadUserInteraction(type, data); - }), - ), -); diff --git a/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts b/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts deleted file mode 100644 index 07ce832e59f2..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/dropdownAction.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { AccountBox } from '../../../ui-utils/client/lib/AccountBox'; - -export const onAdded = async (button: IUIActionButton): Promise<void> => { - const { appId, actionId, labelI18n, context } = button; - await AccountBox.addItem({ - ...button, - name: button.labelI18n, - appId, - actionId, - labelI18n, - context, - isAppButtonItem: true, - }); -}; -export const onRemoved = async (button: IUIActionButton): Promise<void> => { - const { appId, actionId, labelI18n, context } = button; - await AccountBox.deleteItem({ - ...button, - name: button.labelI18n, - appId, - actionId, - labelI18n, - context, - isAppButtonItem: true, - }); -}; diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts b/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts deleted file mode 100644 index 29a45ded6a1d..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/messageAction.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { MessageAction } from '../../../ui-utils/client'; -import { messageArgs } from '../../../../client/lib/utils/messageArgs'; -import { triggerActionButtonAction } from '../ActionManager'; -import { applyButtonFilters } from './lib/applyButtonFilters'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -// eslint-disable-next-line no-void -export const onAdded = (button: IUIActionButton): void => - MessageAction.addButton({ - id: getIdForActionButton(button), - icon: '' as any, - label: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), - context: button.when?.messageActionContext || ['message', 'message-mobile', 'threads', 'starred'], - condition({ room }) { - return applyButtonFilters(button, room); - }, - action(_, props) { - const { message = messageArgs(this).msg } = props; - void triggerActionButtonAction({ - rid: message.rid, - mid: message._id, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context }, - }); - }, - }); - -export const onRemoved = (button: IUIActionButton): void => MessageAction.removeButton(getIdForActionButton(button)); diff --git a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts b/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts deleted file mode 100644 index d07ecbc23ad8..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/messageBox.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { ChatRoom } from '../../../models/client'; -import { messageBox } from '../../../ui-utils/client'; -import { applyButtonFilters } from './lib/applyButtonFilters'; -import { triggerActionButtonAction } from '../ActionManager'; -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { RoomManager } from '../../../../client/lib/RoomManager'; -import { asReactiveSource } from '../../../../client/lib/tracker'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -export const onAdded = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void messageBox.actions.add('Apps', Utilities.getI18nKeyForApp(button.labelI18n, button.appId), { - id: getIdForActionButton(button), - // icon: button.icon || '', - condition() { - return applyButtonFilters( - button, - ChatRoom.findOne( - asReactiveSource( - (cb) => RoomManager.on('changed', cb), - () => RoomManager.opened, - ), - ), - ); - }, - action(params) { - void triggerActionButtonAction({ - rid: params.rid, - tmid: params.tmid, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context, message: params.chat.composer?.text }, - }); - }, - }); - -export const onRemoved = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void messageBox.actions.remove('Apps', new RegExp(getIdForActionButton(button))); diff --git a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts b/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts deleted file mode 100644 index 26352cd9b59a..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/tabbar.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; - -import { addAction, deleteAction } from '../../../../client/views/room/lib/Toolbox'; -import { Utilities } from '../../../../ee/lib/misc/Utilities'; -import { triggerActionButtonAction } from '../ActionManager'; -import { applyButtonFilters } from './lib/applyButtonFilters'; - -const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; - -export const onAdded = (button: IUIActionButton): void => - // eslint-disable-next-line no-void - void addAction(getIdForActionButton(button), ({ room }) => - applyButtonFilters(button, room) - ? { - id: button.actionId, - icon: undefined, // Apps won't provide icons for now - order: 300, // Make sure the button only shows up inside the room toolbox - title: Utilities.getI18nKeyForApp(button.labelI18n, button.appId), - // Filters were applied in the applyButtonFilters function - // if the code made it this far, the button should be shown - groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], - action: (): any => - triggerActionButtonAction({ - rid: room._id, - actionId: button.actionId, - appId: button.appId, - payload: { context: button.context }, - }), - } - : null, - ); - -export const onRemoved = (button: IUIActionButton): boolean => deleteAction(getIdForActionButton(button)); diff --git a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts index 47fdbb311765..6fd101864bc8 100644 --- a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts +++ b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts @@ -66,7 +66,7 @@ export type MessageActionConfig = { chat, autoTranslateOptions, }: { - message?: IMessage & Partial<ITranslatedMessage>; + message: IMessage & Partial<ITranslatedMessage>; tabbar: ToolboxContextValue; room?: IRoom; chat: ContextType<typeof ChatContext>; diff --git a/apps/meteor/app/ui-utils/client/lib/messageBox.ts b/apps/meteor/app/ui-utils/client/lib/messageBox.ts index e5cd105741de..3f3c545af57e 100644 --- a/apps/meteor/app/ui-utils/client/lib/messageBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/messageBox.ts @@ -3,7 +3,7 @@ import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ChatAPI } from '../../../../client/lib/chats/ChatAPI'; -type MessageBoxAction = { +export type MessageBoxAction = { label: TranslationKey; id: string; icon?: string; diff --git a/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx b/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx index f347f1717dfb..1a2370ccad18 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitHandleAction.tsx @@ -5,17 +5,19 @@ import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/ // import React, { Context, FC, useMemo } from 'react'; import type { UiKitPayload, UIKitActionEvent } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; + +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; // import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; // import { useEndpoint } from '@rocket.chat/ui-contexts'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; -const useUIKitHandleAction = <S extends UiKitPayload>(state: S): ((event: UIKitActionEvent) => Promise<void>) => - useMutableCallback(async ({ blockId, value, appId, actionId }) => { +const useUIKitHandleAction = <S extends UiKitPayload>(state: S): ((event: UIKitActionEvent) => Promise<void>) => { + const actionManager = useUiKitActionManager(); + return useMutableCallback(async ({ blockId, value, appId, actionId }) => { if (!appId) { throw new Error('useUIKitHandleAction - invalid appId'); } - return ActionManager.triggerBlockAction({ + return actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: state.viewId || state.appId, @@ -26,5 +28,6 @@ const useUIKitHandleAction = <S extends UiKitPayload>(state: S): ((event: UIKitA blockId, }); }); +}; export { useUIKitHandleAction }; diff --git a/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx b/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx index 85b5c24eb000..b676d23987ea 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitHandleClose.tsx @@ -6,28 +6,30 @@ import type { UIKitInteractionType } from '@rocket.chat/apps-engine/definition/u import type { UiKitPayload } from '@rocket.chat/core-typings'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch } from '@rocket.chat/ui-contexts'; + +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; // import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer'; // import { useEndpoint } from '@rocket.chat/ui-contexts'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; - // eslint-disable-next-line @typescript-eslint/no-unused-vars const emptyFn = (_error: any, _result: UIKitInteractionType | void): void => undefined; const useUIKitHandleClose = <S extends UiKitPayload>(state: S, fn = emptyFn): (() => Promise<void | UIKitInteractionType>) => { + const actionManager = useUiKitActionManager(); const dispatchToastMessage = useToastMessageDispatch(); return useMutableCallback(() => - ActionManager.triggerCancel({ - appId: state.appId, - viewId: state.viewId, - view: { - ...state, - id: state.viewId, - // state: groupStateByBlockId(values), - }, - isCleared: true, - }) + actionManager + .triggerCancel({ + appId: state.appId, + viewId: state.viewId, + view: { + ...state, + id: state.viewId, + // state: groupStateByBlockId(values), + }, + isCleared: true, + }) .then((result) => fn(undefined, result)) .catch((error) => { dispatchToastMessage({ type: 'error', message: error }); diff --git a/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx b/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx index 2ada5f661787..26b329f2ea60 100644 --- a/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx +++ b/apps/meteor/client/UIKit/hooks/useUIKitStateManager.tsx @@ -3,9 +3,10 @@ import { isErrorType } from '@rocket.chat/core-typings'; import { useSafely } from '@rocket.chat/fuselage-hooks'; import { useEffect, useState } from 'react'; -import * as ActionManager from '../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../hooks/useUiKitActionManager'; const useUIKitStateManager = <S extends UiKitPayload>(initialState: S): S => { + const actionManager = useUiKitActionManager(); const [state, setState] = useSafely(useState(initialState)); const { viewId } = state; @@ -22,10 +23,10 @@ const useUIKitStateManager = <S extends UiKitPayload>(initialState: S): S => { setState(rest as any); }; - ActionManager.on(viewId, handleUpdate); + actionManager.on(viewId, handleUpdate); return (): void => { - ActionManager.off(viewId, handleUpdate); + actionManager.off(viewId, handleUpdate); }; }, [setState, viewId]); diff --git a/apps/meteor/client/components/message/toolbox/Toolbox.tsx b/apps/meteor/client/components/message/toolbox/Toolbox.tsx index ee1a0bc2c1b8..f957c75a81c3 100644 --- a/apps/meteor/client/components/message/toolbox/Toolbox.tsx +++ b/apps/meteor/client/components/message/toolbox/Toolbox.tsx @@ -2,15 +2,15 @@ import type { IMessage, IRoom, ISubscription, ITranslatedMessage } from '@rocket import { isThreadMessage, isRoomFederated } from '@rocket.chat/core-typings'; import { MessageToolbox, MessageToolboxItem } from '@rocket.chat/fuselage'; import { useFeaturePreview } from '@rocket.chat/ui-client'; -import { useUser, useSettings, useTranslation } from '@rocket.chat/ui-contexts'; +import { useUser, useSettings, useTranslation, useMethod } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import type { ReactElement } from 'react'; import React, { memo, useMemo } from 'react'; import type { MessageActionContext } from '../../../../app/ui-utils/client/lib/MessageAction'; import { MessageAction } from '../../../../app/ui-utils/client/lib/MessageAction'; -import { sdk } from '../../../../app/utils/client/lib/SDKClient'; import { useEmojiPickerData } from '../../../contexts/EmojiPickerContext'; +import { useMessageActionAppsActionButtons } from '../../../hooks/useAppActionButtons'; import EmojiElement from '../../../views/composer/EmojiPicker/EmojiElement'; import { useIsSelecting } from '../../../views/room/MessageList/contexts/SelectedMessagesContext'; import { useAutoTranslate } from '../../../views/room/MessageList/hooks/useAutoTranslate'; @@ -52,6 +52,8 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): const quickReactionsEnabled = useFeaturePreview('quickReactions'); + const setReaction = useMethod('setReaction'); + const context = getMessageContext(message, room, messageContext); const mapSettings = useMemo(() => Object.fromEntries(settings.map((setting) => [setting._id, setting.value])), [settings]); @@ -59,6 +61,8 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): const chat = useChat(); const { quickReactions, addRecentEmoji } = useEmojiPickerData(); + const actionButtonApps = useMessageActionAppsActionButtons(context); + const actionsQueryResult = useQuery(['rooms', room._id, 'messages', message._id, 'actions'] as const, async () => { const messageActions = await MessageAction.getButtons( { message, room, user: user ?? undefined, subscription, settings: mapSettings, chat }, @@ -87,7 +91,7 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): const isReactionAllowed = actionsQueryResult.data?.message.find(({ id }) => id === 'reaction-message'); const handleSetReaction = (emoji: string) => { - sdk.call('setReaction', `:${emoji}:`, message._id); + setReaction(`:${emoji}:`, message._id); addRecentEmoji(emoji); }; @@ -98,24 +102,23 @@ const Toolbox = ({ message, messageContext, room, subscription }: ToolboxProps): quickReactions.slice(0, 3).map(({ emoji, image }) => { return <EmojiElement small key={emoji} title={emoji} emoji={emoji} image={image} onClick={() => handleSetReaction(emoji)} />; })} - {actionsQueryResult.data?.message.map((action) => ( - <MessageToolboxItem - onClick={(e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions })} - key={action.id} - icon={action.icon} - title={t(action.label)} - data-qa-id={action.label} - data-qa-type='message-action-menu' - /> - ))} - {(actionsQueryResult.data?.menu.length ?? 0) > 0 && ( + {actionsQueryResult.isSuccess && + actionsQueryResult.data.message.map((action) => ( + <MessageToolboxItem + onClick={(e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions })} + key={action.id} + icon={action.icon} + title={t(action.label)} + data-qa-id={action.label} + data-qa-type='message-action-menu' + /> + ))} + {actionsQueryResult.isSuccess && actionsQueryResult.data.menu.length > 0 && ( <MessageActionMenu - options={ - actionsQueryResult.data?.menu.map((action) => ({ - ...action, - action: (e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions }), - })) ?? [] - } + options={[...actionsQueryResult.data?.menu, ...(actionButtonApps.data ?? [])].filter(Boolean).map((action) => ({ + ...action, + action: (e): void => action.action(e, { message, tabbar: toolbox, room, chat, autoTranslateOptions }), + }))} data-qa-type='message-action-menu-options' /> )} diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts new file mode 100644 index 000000000000..1f539c49b002 --- /dev/null +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -0,0 +1,201 @@ +import type { IUIActionButton, UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui'; +import { useEndpoint, useStream, useUserId } from '@rocket.chat/ui-contexts'; +import type { UseQueryResult } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useEffect, useRef, useMemo } from 'react'; + +import { applyButtonFilters } from '../../app/ui-message/client/actionButtons/lib/applyButtonFilters'; +import type { MessageActionConfig, MessageActionContext } from '../../app/ui-utils/client/lib/MessageAction'; +import type { MessageBoxAction } from '../../app/ui-utils/client/lib/messageBox'; +import { Utilities } from '../../ee/lib/misc/Utilities'; +import type { GenericMenuItemProps } from '../components/GenericMenu/GenericMenuItem'; +import { useRoom } from '../views/room/contexts/RoomContext'; +import type { ToolboxAction } from '../views/room/lib/Toolbox'; +import { useUiKitActionManager } from './useUiKitActionManager'; + +const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; + +export const useAppActionButtons = (context?: `${UIActionButtonContext}`) => { + const stream = useRef<() => void>(); + const queryClient = useQueryClient(); + + const apps = useStream('apps'); + const uid = useUserId(); + + useEffect(() => () => stream.current?.(), []); + + useQuery(['apps', 'stream', 'actionButtons', uid], () => { + if (!uid) { + return []; + } + stream.current?.(); + stream.current = apps('actions/changed', () => { + queryClient.invalidateQueries(['apps', 'actionButtons']); + }); + + return []; + }); + + const getActionButtons = useEndpoint('GET', '/apps/actionButtons'); + + const result = useQuery(['apps', 'actionButtons'], () => getActionButtons(), { + ...(context && { + select: (data) => data.filter((button) => button.context === context), + }), + }); + return result; +}; + +export const useMessageboxAppsActionButtons = () => { + const result = useAppActionButtons('messageBoxAction'); + const actionManager = useUiKitActionManager(); + const room = useRoom(); + + const data = useMemo( + () => + result.data + ?.filter((action) => { + return applyButtonFilters(action, room); + }) + .map((action) => { + const item: MessageBoxAction = { + id: getIdForActionButton(action), + label: Utilities.getI18nKeyForApp(action.labelI18n, action.appId), + action: (params) => { + void actionManager.triggerActionButtonAction({ + rid: params.rid, + tmid: params.tmid, + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context, message: params.chat.composer?.text }, + }); + }, + }; + + return item; + }), + [actionManager, result.data, room], + ); + return { + ...result, + data, + } as UseQueryResult<MessageBoxAction[]>; +}; + +export const useUserDropdownAppsActionButtons = () => { + const result = useAppActionButtons('userDropdownAction'); + const actionManager = useUiKitActionManager(); + + const data = useMemo( + () => + result.data + ?.filter((action) => { + return applyButtonFilters(action); + }) + .map((action, key) => { + return { + id: action.actionId + key, + // icon: action.icon as GenericMenuItemProps['icon'], + content: action.labelI18n, + onClick: () => { + actionManager.triggerActionButtonAction({ + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context }, + }); + }, + }; + }), + [actionManager, result.data], + ); + return { + ...result, + data, + } as UseQueryResult<GenericMenuItemProps[]>; +}; + +export const useRoomActionAppsActionButtons = (context?: MessageActionContext) => { + const result = useAppActionButtons('roomAction'); + const actionManager = useUiKitActionManager(); + const room = useRoom(); + const data = useMemo( + () => + result.data + ?.filter((action) => { + if (context && ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'].includes(context)) { + return false; + } + return applyButtonFilters(action, room); + }) + .map((action) => { + const item: [string, ToolboxAction] = [ + action.actionId, + { + id: action.actionId, + icon: undefined as any, // Apps won't provide icons for now + order: 300, // Make sure the button only shows up inside the room toolbox + title: Utilities.getI18nKeyForApp(action.labelI18n, action.appId), + groups: ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'], + // Filters were applied in the applyButtonFilters function + // if the code made it this far, the button should be shown + action: () => + void actionManager.triggerActionButtonAction({ + rid: room._id, + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context }, + }), + }, + ]; + return item; + }), + [actionManager, context, result.data, room], + ); + return { + ...result, + data, + } as UseQueryResult<[string, ToolboxAction][]>; +}; + +export const useMessageActionAppsActionButtons = (context?: MessageActionContext) => { + const result = useAppActionButtons('messageAction'); + const actionManager = useUiKitActionManager(); + const room = useRoom(); + + const data = useMemo( + () => + result.data + ?.filter((action) => { + if ( + context && + !(action.when?.messageActionContext || ['message', 'message-mobile', 'threads', 'starred']).includes(context as any) + ) { + return false; + } + return applyButtonFilters(action, room); + }) + .map((action) => { + const item: MessageActionConfig = { + icon: undefined as any, + id: getIdForActionButton(action), + label: Utilities.getI18nKeyForApp(action.labelI18n, action.appId), + action: (_, params) => { + void actionManager.triggerActionButtonAction({ + rid: params.message.rid, + tmid: params.message.tmid, + actionId: action.actionId, + appId: action.appId, + payload: { context: action.context }, + }); + }, + }; + + return item; + }), + [actionManager, context, result.data, room], + ); + return { + ...result, + data, + } as UseQueryResult<MessageActionConfig[]>; +}; diff --git a/apps/meteor/client/hooks/useAppSlashCommands.ts b/apps/meteor/client/hooks/useAppSlashCommands.ts new file mode 100644 index 000000000000..548084d4a00e --- /dev/null +++ b/apps/meteor/client/hooks/useAppSlashCommands.ts @@ -0,0 +1,45 @@ +import { useEndpoint, useStream, useUserId } from '@rocket.chat/ui-contexts'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useEffect } from 'react'; + +import { slashCommands } from '../../app/utils/lib/slashCommand'; + +const stop = (...args: (() => void)[]) => args.forEach((s) => s()); + +export const useAppSlashCommands = () => { + const queryClient = useQueryClient(); + + const apps = useStream('apps'); + const uid = useUserId(); + + useEffect(() => { + if (!uid) { + return; + } + return stop( + apps('command/added', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + + apps('command/updated', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + + apps('command/removed', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + + apps('command/disabled', () => { + queryClient.invalidateQueries(['apps', 'slashCommands']); + }), + ); + }, [apps, queryClient, uid]); + + const getSlashCommands = useEndpoint('GET', '/v1/commands.list'); + + useQuery(['apps', 'slashCommands'], () => getSlashCommands(), { + onSuccess(data) { + data.commands.forEach((command) => slashCommands.add(command)); + }, + }); +}; diff --git a/apps/meteor/client/hooks/useAppUiKitInteraction.ts b/apps/meteor/client/hooks/useAppUiKitInteraction.ts new file mode 100644 index 000000000000..b78a35456736 --- /dev/null +++ b/apps/meteor/client/hooks/useAppUiKitInteraction.ts @@ -0,0 +1,27 @@ +import type { UIKitInteractionType } from '@rocket.chat/apps-engine/definition/uikit'; +import { useStream, useUserId } from '@rocket.chat/ui-contexts'; +import { useEffect } from 'react'; + +export const useAppUiKitInteraction = ( + handlePayloadUserInteraction: ( + type: UIKitInteractionType, + data: { + triggerId: string; + appId: string; + }, + ) => void, +) => { + const notifyUser = useStream('notify-user'); + + const uid = useUserId(); + + useEffect(() => { + if (!uid) { + return; + } + + return notifyUser(`${uid}/uiInteraction`, ({ type, ...data }) => { + handlePayloadUserInteraction(type, data); + }); + }, [notifyUser, uid, handlePayloadUserInteraction]); +}; diff --git a/apps/meteor/client/hooks/useUiKitActionManager.ts b/apps/meteor/client/hooks/useUiKitActionManager.ts new file mode 100644 index 000000000000..d6bdab88004f --- /dev/null +++ b/apps/meteor/client/hooks/useUiKitActionManager.ts @@ -0,0 +1,11 @@ +import { useContext } from 'react'; + +import { ActionManagerContext } from '../contexts/ActionManagerContext'; + +export const useUiKitActionManager = () => { + const actionManager = useContext(ActionManagerContext); + if (!actionManager) { + throw new Error('ActionManagerContext is not provided'); + } + return actionManager; +}; diff --git a/apps/meteor/client/providers/ActionManagerProvider.tsx b/apps/meteor/client/providers/ActionManagerProvider.tsx index b76d99b06af9..dd84eec234ee 100644 --- a/apps/meteor/client/providers/ActionManagerProvider.tsx +++ b/apps/meteor/client/providers/ActionManagerProvider.tsx @@ -3,12 +3,19 @@ import React from 'react'; import * as ActionManager from '../../app/ui-message/client/ActionManager'; import { ActionManagerContext } from '../contexts/ActionManagerContext'; +import { useAppActionButtons } from '../hooks/useAppActionButtons'; +import { useAppSlashCommands } from '../hooks/useAppSlashCommands'; +import { useAppUiKitInteraction } from '../hooks/useAppUiKitInteraction'; type ActionManagerProviderProps = { children?: ReactNode; }; const ActionManagerProvider = ({ children }: ActionManagerProviderProps): ReactElement => { + useAppActionButtons(); + useAppSlashCommands(); + useAppUiKitInteraction(ActionManager.handlePayloadUserInteraction); + return <ActionManagerContext.Provider value={ActionManager}>{children}</ActionManagerContext.Provider>; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx index f6179bb93d26..72b9b90419a5 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx @@ -59,11 +59,7 @@ export const useAdministrationMenu = () => { const showApps = hasAccessMarketplacePermission || hasManageAppsPermission || !!appBoxItems.length; const administrationItems = useAdministrationItems({ accountBoxItems: adminBoxItems, showWorkspace }); - const appItems = useAppsItems({ - appBoxItems, - appsManagementAllowed: hasManageAppsPermission, - showMarketplace: hasAccessMarketplacePermission || hasManageAppsPermission, - }); + const appItems = useAppsItems(); const auditItems = useAuditItems({ showAudit: hasAuditPermission, showAuditLog: hasAuditLogPermission }); const sections = [ diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx index a6c77c55d284..6ecd34b62d5d 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx @@ -1,21 +1,21 @@ import { Badge, Skeleton } from '@rocket.chat/fuselage'; -import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRoute, usePermission } from '@rocket.chat/ui-contexts'; import React from 'react'; -import { triggerActionButtonAction } from '../../../../../app/ui-message/client/ActionManager'; -import type { IAppAccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; +import { useUserDropdownAppsActionButtons } from '../../../../hooks/useAppActionButtons'; import { useAppRequestStats } from '../../../../views/marketplace/hooks/useAppRequestStats'; -type useAppsItemsProps = { - appBoxItems: IAppAccountBoxItem[]; - appsManagementAllowed?: boolean; - showMarketplace?: boolean; -}; - -export const useAppsItems = ({ appBoxItems, appsManagementAllowed, showMarketplace }: useAppsItemsProps): GenericMenuItemProps[] => { +export const useAppsItems = (): GenericMenuItemProps[] => { const t = useTranslation(); + const appBoxItems = useUserDropdownAppsActionButtons(); + + const hasManageAppsPermission = usePermission('manage-apps'); + const hasAccessMarketplacePermission = usePermission('access-marketplace'); + + const showMarketplace = hasAccessMarketplacePermission || hasManageAppsPermission; + const marketplaceRoute = useRoute('marketplace'); const page = 'list'; @@ -46,33 +46,16 @@ export const useAppsItems = ({ appBoxItems, appsManagementAllowed, showMarketpla addon: ( <> {appRequestStats.isLoading && <Skeleton variant='circle' height={16} width={16} />} - {appRequestStats.isSuccess && appRequestStats.data.data.totalUnseen > 0 && ( - <Badge variant='primary'>{appRequestStats.data.data.totalUnseen}</Badge> + {appRequestStats.isSuccess && appRequestStats.data.totalUnseen > 0 && ( + <Badge variant='primary'>{appRequestStats.data.totalUnseen}</Badge> )} </> ), }; - const appItems: GenericMenuItemProps[] = appBoxItems.map((item: IAppAccountBoxItem, key: number) => { - const action = () => { - triggerActionButtonAction({ - rid: '', - mid: '', - actionId: item.actionId, - appId: item.appId, - payload: { context: item.context }, - }); - }; - return { - id: item.actionId + key, - icon: item.icon as GenericMenuItemProps['icon'], - content: (t.has(item.name) && t(item.name)) || item.name, - onClick: action, - }; - }); return [ ...(showMarketplace ? marketPlaceItems : []), - ...(appsManagementAllowed ? [appsManagementItem] : []), - ...(appBoxItems.length ? appItems : []), + ...(hasManageAppsPermission ? [appsManagementItem] : []), + ...(appBoxItems.isSuccess ? appBoxItems.data : []), ]; }; diff --git a/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx b/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx index 58c055b7f6bc..acfc04e5f33f 100644 --- a/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx +++ b/apps/meteor/client/views/marketplace/components/MarketplaceRequestBadge.tsx @@ -11,11 +11,11 @@ const MarketplaceRequestBadge = () => { if (requestStatsResult.isError) return null; - if (!requestStatsResult.data.data.totalUnseen) { + if (!requestStatsResult.data.totalUnseen) { return null; } - return <Badge variant='primary'>{requestStatsResult.data.data.totalUnseen}</Badge>; + return <Badge variant='primary'>{requestStatsResult.data.totalUnseen}</Badge>; }; export default MarketplaceRequestBadge; diff --git a/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts b/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts index 70e692eba5e8..af25282b7e53 100644 --- a/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts +++ b/apps/meteor/client/views/marketplace/hooks/useAppRequestStats.ts @@ -8,7 +8,7 @@ export const useAppRequestStats = () => { return useQuery({ queryKey: ['app-requests-stats'], - queryFn: async () => fetchRequestStats(), + queryFn: async () => (await fetchRequestStats()).data, refetchOnWindowFocus: false, retry: false, enabled: canManageApp, diff --git a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx index 6bdefce4ff9e..c9c17af46f32 100644 --- a/apps/meteor/client/views/modal/uikit/UiKitModal.tsx +++ b/apps/meteor/client/views/modal/uikit/UiKitModal.tsx @@ -6,7 +6,7 @@ import type { LayoutBlock } from '@rocket.chat/ui-kit'; import type { ContextType, ReactElement, ReactEventHandler } from 'react'; import React from 'react'; -import * as ActionManager from '../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../hooks/useUiKitActionManager'; import { detectEmoji } from '../../../lib/utils/detectEmoji'; import ModalBlock from './ModalBlock'; import type { ActionManagerState } from './hooks/useActionManagerState'; @@ -14,6 +14,7 @@ import { useActionManagerState } from './hooks/useActionManagerState'; import { useValues } from './hooks/useValues'; const UiKitModal = (props: ActionManagerState): ReactElement => { + const actionManager = useUiKitActionManager(); const state = useActionManagerState(props); const { appId, viewId, mid: _mid, errors, view } = state; @@ -37,7 +38,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { }; const debouncedBlockAction = useDebouncedCallback((actionId, appId, value, blockId, mid) => { - ActionManager.triggerBlockAction({ + actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -57,7 +58,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { if (Array.isArray(dispatchActionConfig) && dispatchActionConfig.includes('on_character_entered')) { debouncedBlockAction(actionId, appId, value, blockId, mid); } else { - ActionManager.triggerBlockAction({ + actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -86,7 +87,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { const handleSubmit = useMutableCallback((e) => { prevent(e); - ActionManager.triggerSubmitView({ + actionManager.triggerSubmitView({ viewId, appId, payload: { @@ -101,7 +102,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { const handleCancel = useMutableCallback((e) => { prevent(e); - ActionManager.triggerCancel({ + actionManager.triggerCancel({ viewId, appId, view: { @@ -113,7 +114,7 @@ const UiKitModal = (props: ActionManagerState): ReactElement => { }); const handleClose = useMutableCallback(() => { - ActionManager.triggerCancel({ + actionManager.triggerCancel({ viewId, appId, view: { diff --git a/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts b/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts index 8f6ee7bb26d2..d2d6d0112015 100644 --- a/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts +++ b/apps/meteor/client/views/modal/uikit/hooks/useActionManagerState.ts @@ -1,7 +1,7 @@ import type { IUIKitSurface } from '@rocket.chat/apps-engine/definition/uikit'; import { useEffect, useState } from 'react'; -import * as ActionManager from '../../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../../hooks/useUiKitActionManager'; export type ActionManagerState = { viewId: string; @@ -13,6 +13,7 @@ export type ActionManagerState = { }; export const useActionManagerState = (initialState: ActionManagerState) => { + const actionManager = useUiKitActionManager(); const [state, setState] = useState(initialState); const { viewId } = state; @@ -27,10 +28,10 @@ export const useActionManagerState = (initialState: ActionManagerState) => { setState({ ...data, type, errors }); }; - ActionManager.on(viewId, handleUpdate); + actionManager.on(viewId, handleUpdate); return () => { - ActionManager.off(viewId, handleUpdate); + actionManager.off(viewId, handleUpdate); }; }, [viewId]); diff --git a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx index 406160124696..923996adb838 100644 --- a/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx +++ b/apps/meteor/client/views/room/components/body/composer/messageBox/MessageBoxActionsToolbar/ActionsToolbarDropdown.tsx @@ -5,6 +5,7 @@ import type { ComponentProps, ReactNode } from 'react'; import React, { useRef, Fragment } from 'react'; import { messageBox } from '../../../../../../../../app/ui-utils/client'; +import { useMessageboxAppsActionButtons } from '../../../../../../../hooks/useAppActionButtons'; import type { ChatAPI } from '../../../../../../../lib/chats/ChatAPI'; import { useDropdownVisibility } from '../../../../../../../sidebar/header/hooks/useDropdownVisibility'; import { useChat } from '../../../../../contexts/ChatContext'; @@ -35,7 +36,16 @@ const ActionsToolbarDropdown = ({ isRecording, rid, tmid, actions, ...props }: A const { isVisible, toggle } = useDropdownVisibility({ reference, target }); - const groups = messageBox.actions.get(); + const apps = useMessageboxAppsActionButtons(); + + const groups = { + ...(apps.isSuccess && + apps.data.length > 0 && { + Apps: apps.data, + }), + ...messageBox.actions.get(), + }; + const messageBoxActions = Object.entries(groups).map(([name, group]) => { const items = group.map((item) => ({ icon: item.icon, diff --git a/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx b/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx index f1f9152c01ea..b50817dae536 100644 --- a/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx +++ b/apps/meteor/client/views/room/contextualBar/Apps/AppsWithData.tsx @@ -16,7 +16,7 @@ import type { Block } from '@rocket.chat/ui-kit'; import type { Dispatch, SyntheticEvent, ContextType } from 'react'; import React, { memo, useState, useEffect, useReducer } from 'react'; -import { triggerBlockAction, triggerCancel, triggerSubmitView, on, off } from '../../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../../hooks/useUiKitActionManager'; import { useTabBarClose } from '../../contexts/ToolboxContext'; import Apps from './Apps'; @@ -101,6 +101,7 @@ const AppsWithData = ({ payload: IUIKitContextualBarInteraction; appId: string; }): JSX.Element => { + const actionManager = useUiKitActionManager(); const closeTabBar = useTabBarClose(); const [state, setState] = useState<ViewState>(payload); @@ -118,10 +119,10 @@ const AppsWithData = ({ setState(data as IUIKitContextualBarInteraction); }; - on(viewId, handleUpdate); + actionManager.on(viewId, handleUpdate); return (): void => { - off(viewId, handleUpdate); + actionManager.off(viewId, handleUpdate); }; }, [state, viewId]); @@ -141,7 +142,7 @@ const AppsWithData = ({ }; const debouncedBlockAction = useDebouncedCallback(({ actionId, appId, value, blockId }: ActionParams) => { - triggerBlockAction({ + actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -158,7 +159,7 @@ const AppsWithData = ({ if (Array.isArray(dispatchActionConfig) && dispatchActionConfig.includes(InputElementDispatchAction.ON_CHARACTER_ENTERED)) { await debouncedBlockAction({ actionId, appId, value, blockId }); } else { - await triggerBlockAction({ + await actionManager.triggerBlockAction({ container: { type: UIKitIncomingInteractionContainerType.VIEW, id: viewId, @@ -187,7 +188,7 @@ const AppsWithData = ({ const handleSubmit = useMutableCallback((e) => { prevent(e); closeTabBar(); - triggerSubmitView({ + actionManager.triggerSubmitView({ viewId, appId, payload: { @@ -203,7 +204,7 @@ const AppsWithData = ({ const handleCancel = useMutableCallback((e) => { prevent(e); closeTabBar(); - return triggerCancel({ + return actionManager.triggerCancel({ appId, viewId, view: { @@ -217,7 +218,7 @@ const AppsWithData = ({ const handleClose = useMutableCallback((e) => { prevent(e); closeTabBar(); - return triggerCancel({ + return actionManager.triggerCancel({ appId, viewId, view: { diff --git a/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts b/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts index 238ee807744f..6afa6c3a6f84 100644 --- a/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts +++ b/apps/meteor/client/views/room/hooks/useAppsContextualBar.ts @@ -2,7 +2,7 @@ import type { IUIKitContextualBarInteraction } from '@rocket.chat/apps-engine/de import { useRouteParameter } from '@rocket.chat/ui-contexts'; import { useEffect, useState } from 'react'; -import { getUserInteractionPayloadByViewId } from '../../../../app/ui-message/client/ActionManager'; +import { useUiKitActionManager } from '../../../hooks/useUiKitActionManager'; import { useRoom } from '../contexts/RoomContext'; type AppsContextualBarData = { @@ -14,6 +14,7 @@ type AppsContextualBarData = { export const useAppsContextualBar = (): AppsContextualBarData | undefined => { const [payload, setPayload] = useState<IUIKitContextualBarInteraction>(); + const actionManager = useUiKitActionManager(); const [appId, setAppId] = useState<string>(); const { _id: roomId } = useRoom(); @@ -22,7 +23,7 @@ export const useAppsContextualBar = (): AppsContextualBarData | undefined => { useEffect(() => { if (viewId) { - setPayload(getUserInteractionPayloadByViewId(viewId) as IUIKitContextualBarInteraction); + setPayload(actionManager.getUserInteractionPayloadByViewId(viewId) as IUIKitContextualBarInteraction); } if (payload?.appId) { @@ -33,7 +34,7 @@ export const useAppsContextualBar = (): AppsContextualBarData | undefined => { setPayload(undefined); setAppId(undefined); }; - }, [viewId, payload?.appId]); + }, [viewId, payload?.appId, actionManager]); if (viewId && payload && appId) { return { diff --git a/apps/meteor/client/views/room/providers/ToolboxProvider.tsx b/apps/meteor/client/views/room/providers/ToolboxProvider.tsx index 27fe6abbb176..865c9c248bf3 100644 --- a/apps/meteor/client/views/room/providers/ToolboxProvider.tsx +++ b/apps/meteor/client/views/room/providers/ToolboxProvider.tsx @@ -4,6 +4,7 @@ import { useUserId, useSetting, useRouter, useRouteParameter } from '@rocket.cha import type { ReactNode } from 'react'; import React, { useMemo } from 'react'; +import { useRoomActionAppsActionButtons } from '../../../hooks/useAppActionButtons'; import type { ToolboxContextValue } from '../contexts/ToolboxContext'; import { ToolboxContext } from '../contexts/ToolboxContext'; import type { Store } from '../lib/Toolbox/generator'; @@ -103,9 +104,11 @@ const ToolboxProvider = ({ children, room }: { children: ReactNode; room: IRoom [listen, list, activeTabBar, open, close, openRoomInfo], ); + const appActions = useRoomActionAppsActionButtons(); + return ( <ToolboxContext.Provider value={contextValue}> - {actions + {[...actions, ...(appActions.data ?? [])] .filter( ([, action]) => uid || (allowAnonymousRead && action.hasOwnProperty('anonymous') && (action as ToolboxActionConfig).anonymous), ) diff --git a/apps/meteor/ee/client/apps/communication/websockets.js b/apps/meteor/ee/client/apps/communication/websockets.js index efb76a46f7f6..e8060539a665 100644 --- a/apps/meteor/ee/client/apps/communication/websockets.js +++ b/apps/meteor/ee/client/apps/communication/websockets.js @@ -1,7 +1,6 @@ import { Emitter } from '@rocket.chat/emitter'; import { CachedCollectionManager } from '../../../../app/ui-cached-collection/client'; -import { loadButtons } from '../../../../app/ui-message/client/ActionButtonSyncer'; import { slashCommands } from '../../../../app/utils/client'; import { sdk } from '../../../../app/utils/client/lib/SDKClient'; @@ -72,5 +71,5 @@ export class AppWebsocketReceiver extends Emitter { delete slashCommands.commands[command]; }; - onActionsChanged = () => loadButtons(); + // onActionsChanged = () => loadButtons(); } diff --git a/ee/packages/ddp-client/src/types/streams.ts b/ee/packages/ddp-client/src/types/streams.ts index 5892371afb1d..fc2fc381570e 100644 --- a/ee/packages/ddp-client/src/types/streams.ts +++ b/ee/packages/ddp-client/src/types/streams.ts @@ -1,4 +1,5 @@ import type { ISetting as AppsSetting } from '@rocket.chat/apps-engine/definition/settings'; +import type { IUIKitInteraction } from '@rocket.chat/apps-engine/definition/uikit'; import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import type { IMessage, @@ -136,7 +137,7 @@ export interface StreamerEvents { { key: `${string}/notification`; args: [INotificationDesktop] }, { key: `${string}/voip.events`; args: [VoipEventDataSignature] }, { key: `${string}/call.hangup`; args: [{ roomId: string }] }, - { key: `${string}/uiInteraction`; args: [unknown] }, + { key: `${string}/uiInteraction`; args: [IUIKitInteraction] }, { key: `${string}/video-conference`; args: [{ action: string; params: { callId: VideoConference['_id']; uid: IUser['_id']; rid: IRoom['_id'] } }]; From bd231a06ba509f17882fb8aaf4c25d418c28fe2c Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Tue, 11 Jul 2023 18:22:38 -0300 Subject: [PATCH 109/149] refactor: Remove Accountbox usage (#29786) --- apps/meteor/app/lib/README.md | 13 --- apps/meteor/app/livechat/client/ui.js | 12 +-- apps/meteor/app/ui-utils/client/index.ts | 1 - .../app/ui-utils/client/lib/AccountBox.ts | 32 ------- .../actions/hooks/useAdministrationItems.tsx | 84 +++++++++++++------ .../actions/hooks/useAdministrationMenu.tsx | 70 ++-------------- .../header/actions/hooks/useAuditItems.tsx | 19 +++-- .../sidebar/header/hooks/useStatusItems.tsx | 10 +-- apps/meteor/client/startup/iframeCommands.ts | 2 +- 9 files changed, 86 insertions(+), 157 deletions(-) diff --git a/apps/meteor/app/lib/README.md b/apps/meteor/app/lib/README.md index 708a9a21790e..5201c4cadf0e 100644 --- a/apps/meteor/app/lib/README.md +++ b/apps/meteor/app/lib/README.md @@ -47,19 +47,6 @@ settingsRegistry.addGroup('Settings_Group', function() { * `enableQuery` - Only enable this setting if the correspondent setting has the value specified * `alert` - Shows an alert message with the given text -### AccountBox - -You can add items to the left upper corner drop menu: -```javascript -AccountBox.addItem({ - name: 'Livechat', - icon: 'icon-chat-empty', - class: 'livechat-manager', - condition: () => { - return RocketChat.authz.hasPermission('view-livechat-manager'); - } -}); -``` ### Functions n/a diff --git a/apps/meteor/app/livechat/client/ui.js b/apps/meteor/app/livechat/client/ui.js index 614b410cda2e..5d8cc1696d44 100644 --- a/apps/meteor/app/livechat/client/ui.js +++ b/apps/meteor/app/livechat/client/ui.js @@ -1,14 +1,4 @@ -import { settings } from '../../settings/client'; -import { hasAllPermission } from '../../authorization/client'; -import { AccountBox, MessageTypes } from '../../ui-utils/client'; - -AccountBox.addItem({ - name: 'Omnichannel', - icon: 'headset', - href: '/omnichannel/current', - sideNav: 'omnichannelFlex', - condition: () => settings.get('Livechat_enabled') && hasAllPermission('view-livechat-manager'), -}); +import { MessageTypes } from '../../ui-utils/client'; MessageTypes.registerType({ id: 'livechat-close', diff --git a/apps/meteor/app/ui-utils/client/index.ts b/apps/meteor/app/ui-utils/client/index.ts index 83a1057a7287..aaa5a0825706 100644 --- a/apps/meteor/app/ui-utils/client/index.ts +++ b/apps/meteor/app/ui-utils/client/index.ts @@ -1,6 +1,5 @@ import './lib/messageActionDefault'; -export { AccountBox } from './lib/AccountBox'; export { MessageAction } from './lib/MessageAction'; export { messageBox } from './lib/messageBox'; export { readMessage } from './lib/readMessages'; diff --git a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts index 3ed3a60b3bf6..c7bda9df1659 100644 --- a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts @@ -1,12 +1,9 @@ import type { IUIActionButton, IUActionButtonWhen } from '@rocket.chat/apps-engine/definition/ui/IUIActionButtonDescriptor'; import type { UserStatus } from '@rocket.chat/core-typings'; -import { ReactiveVar } from 'meteor/reactive-var'; -import { Tracker } from 'meteor/tracker'; import type { TranslationKey, LocationPathname } from '@rocket.chat/ui-contexts'; import type { Icon } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; -import { applyDropdownActionButtonFilters } from '../../../ui-message/client/actionButtons/lib/applyButtonFilters'; import { sdk } from '../../../utils/client/lib/SDKClient'; export interface IAppAccountBoxItem extends IUIActionButton { @@ -30,38 +27,9 @@ export type AccountBoxItem = { export const isAppAccountBoxItem = (item: IAppAccountBoxItem | AccountBoxItem): item is IAppAccountBoxItem => 'isAppButtonItem' in item; class AccountBoxBase { - private items = new ReactiveVar<IAppAccountBoxItem[]>([]); - public setStatus(status: UserStatus, statusText?: string): any { return sdk.rest.post('/v1/users.setStatus', { status, message: statusText }); } - - public async addItem(newItem: IAppAccountBoxItem): Promise<void> { - Tracker.nonreactive(() => { - const actual = this.items.get(); - actual.push(newItem); - this.items.set(actual); - }); - } - - public async deleteItem(item: IAppAccountBoxItem): Promise<void> { - Tracker.nonreactive(() => { - const actual = this.items.get(); - const itemIndex = actual.findIndex((actualItem: IAppAccountBoxItem) => actualItem.appId === item.appId); - actual.splice(itemIndex, 1); - this.items.set(actual); - }); - } - - public getItems(): (IAppAccountBoxItem | AccountBoxItem)[] { - return this.items.get().filter((item: IAppAccountBoxItem | AccountBoxItem) => { - if ('condition' in item) { - return item.condition(); - } - - return applyDropdownActionButtonFilters(item); - }); - } } export const AccountBox = new AccountBoxBase(); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index 97bcb291a37e..c0dc83fb62c1 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -1,8 +1,16 @@ -import { useTranslation, useRoute, useMethod, useSetModal, useRole, useRouter } from '@rocket.chat/ui-contexts'; +import { + useTranslation, + useRoute, + useMethod, + useSetModal, + useRole, + useRouter, + useAtLeastOnePermission, + usePermission, +} from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; -import type { AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; import type { UpgradeTabVariant } from '../../../../../lib/upgradeTab'; import { getUpgradeTabLabel, isFullyFeature } from '../../../../../lib/upgradeTab'; import Emoji from '../../../../components/Emoji'; @@ -10,17 +18,45 @@ import type { GenericMenuItemProps } from '../../../../components/GenericMenu/Ge import RegisterWorkspaceModal from '../../../../views/admin/cloud/modals/RegisterWorkspaceModal'; import { useUpgradeTabParams } from '../../../../views/hooks/useUpgradeTabParams'; -type useAdministrationItemProps = { - accountBoxItems: AccountBoxItem[]; - showWorkspace: boolean; -}; -export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAdministrationItemProps): GenericMenuItemProps[] => { +const ADMIN_PERMISSIONS = [ + 'view-statistics', + 'run-import', + 'view-user-administration', + 'view-room-administration', + 'create-invite-links', + 'manage-cloud', + 'view-logs', + 'manage-sounds', + 'view-federation-data', + 'manage-email-inbox', + 'manage-emoji', + 'manage-outgoing-integrations', + 'manage-own-outgoing-integrations', + 'manage-incoming-integrations', + 'manage-own-incoming-integrations', + 'manage-oauth-apps', + 'access-mailer', + 'manage-user-status', + 'access-permissions', + 'access-setting-permissions', + 'view-privileged-setting', + 'edit-privileged-setting', + 'manage-selected-settings', + 'view-engagement-dashboard', + 'view-moderation-console', +]; + +export const useAdministrationItems = (): GenericMenuItemProps[] => { const router = useRouter(); const t = useTranslation(); + const shouldShowAdminMenu = useAtLeastOnePermission(ADMIN_PERMISSIONS); + const { tabType, trialEndDate, isLoading } = useUpgradeTabParams(); const shouldShowEmoji = isFullyFeature(tabType); + const label = getUpgradeTabLabel(tabType); + const isAdmin = useRole('admin'); const setModal = useSetModal(); @@ -36,8 +72,18 @@ export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAd const adminRoute = useRoute('admin-index'); const upgradeRoute = useRoute('upgrade'); const cloudRoute = useRoute('cloud'); + + const omnichannel = usePermission('view-livechat-manager'); + const showUpgradeItem = !isLoading && tabType; + const omnichannelItem: GenericMenuItemProps = { + id: 'omnichannel', + content: t('Omnichannel'), + icon: 'headset', + onClick: () => router.navigate('/omnichannel/current'), + }; + const upgradeItem: GenericMenuItemProps = { id: 'showUpgradeItem', content: ( @@ -71,24 +117,10 @@ export const useAdministrationItems = ({ accountBoxItems, showWorkspace }: useAd }, }; - const accountBoxItem: GenericMenuItemProps[] = accountBoxItems.map((item, key) => { - const action = () => { - if (item.href) { - router.navigate(item.href); - } - }; - return { - id: `account-box-item-${key}`, - content: t(item.name), - icon: item.icon, - onClick: action, - }; - }); - return [ - ...(showUpgradeItem ? [upgradeItem] : []), - ...(isAdmin ? [adminItem] : []), - ...(showWorkspace ? [workspaceItem] : []), - ...(accountBoxItems.length ? accountBoxItem : []), - ]; + showUpgradeItem && upgradeItem, + isAdmin && adminItem, + omnichannel && omnichannelItem, + shouldShowAdminMenu && workspaceItem, + ].filter(Boolean) as GenericMenuItemProps[]; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx index 72b9b90419a5..5be021cf0b7e 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationMenu.tsx @@ -1,72 +1,20 @@ -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useAtLeastOnePermission, usePermission, useTranslation } from '@rocket.chat/ui-contexts'; +import { useTranslation } from '@rocket.chat/ui-contexts'; -import { AccountBox } from '../../../../../app/ui-utils/client'; -import type { IAppAccountBoxItem, AccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; -import { isAppAccountBoxItem } from '../../../../../app/ui-utils/client/lib/AccountBox'; -import { useHasLicenseModule } from '../../../../../ee/client/hooks/useHasLicenseModule'; -import { useReactiveValue } from '../../../../hooks/useReactiveValue'; +import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; import { useAdministrationItems } from './useAdministrationItems'; import { useAppsItems } from './useAppsItems'; import { useAuditItems } from './useAuditItems'; -const ADMIN_PERMISSIONS = [ - 'view-statistics', - 'run-import', - 'view-user-administration', - 'view-room-administration', - 'create-invite-links', - 'manage-cloud', - 'view-logs', - 'manage-sounds', - 'view-federation-data', - 'manage-email-inbox', - 'manage-emoji', - 'manage-outgoing-integrations', - 'manage-own-outgoing-integrations', - 'manage-incoming-integrations', - 'manage-own-incoming-integrations', - 'manage-oauth-apps', - 'access-mailer', - 'manage-user-status', - 'access-permissions', - 'access-setting-permissions', - 'view-privileged-setting', - 'edit-privileged-setting', - 'manage-selected-settings', - 'view-engagement-dashboard', - 'view-moderation-console', -]; - export const useAdministrationMenu = () => { const t = useTranslation(); - const getAccountBoxItems = useMutableCallback(() => AccountBox.getItems()); - const accountBoxItems = useReactiveValue(getAccountBoxItems); - - const hasAuditLicense = useHasLicenseModule('auditing') === true; - const hasManageAppsPermission = usePermission('manage-apps'); - const hasAccessMarketplacePermission = usePermission('access-marketplace'); - const hasAdminPermission = useAtLeastOnePermission(ADMIN_PERMISSIONS); - const hasAuditPermission = usePermission('can-audit') && hasAuditLicense; - const hasAuditLogPermission = usePermission('can-audit-log') && hasAuditLicense; - - const appBoxItems = accountBoxItems.filter((item): item is IAppAccountBoxItem => isAppAccountBoxItem(item)); - const adminBoxItems = accountBoxItems.filter((item): item is AccountBoxItem => !isAppAccountBoxItem(item)); - const showAdmin = hasAdminPermission || !!adminBoxItems.length; - const showAudit = hasAuditPermission || hasAuditLogPermission; - const showWorkspace = hasAdminPermission; - const showApps = hasAccessMarketplacePermission || hasManageAppsPermission || !!appBoxItems.length; - - const administrationItems = useAdministrationItems({ accountBoxItems: adminBoxItems, showWorkspace }); + const administrationItems = useAdministrationItems(); const appItems = useAppsItems(); - const auditItems = useAuditItems({ showAudit: hasAuditPermission, showAuditLog: hasAuditLogPermission }); - - const sections = [ - { title: t('Administration'), items: administrationItems, permission: showAdmin }, - { title: t('Apps'), items: appItems, permission: showApps }, - { title: t('Audit'), items: auditItems, permission: showAudit }, - ]; + const auditItems = useAuditItems(); - return sections.filter(({ permission }) => permission); + return [ + administrationItems.length && { title: t('Administration'), items: administrationItems }, + appItems.length && { title: t('Apps'), items: appItems }, + auditItems.length && { title: t('Audit'), items: auditItems }, + ].filter(Boolean) as Array<{ title: string; items: GenericMenuItemProps[] }>; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx index 3ed3ba94dcb9..07b2b9bb11c3 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx @@ -1,18 +1,23 @@ -import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; +import { useTranslation, useRoute, usePermission } from '@rocket.chat/ui-contexts'; +import { useHasLicenseModule } from '../../../../../ee/client/hooks/useHasLicenseModule'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; -type useAuditItemsProps = { - showAudit: boolean; - showAuditLog: boolean; -}; +export const useAuditItems = (): GenericMenuItemProps[] => { + const hasAuditLicense = useHasLicenseModule('auditing') === true; + + const hasAuditPermission = usePermission('can-audit') && hasAuditLicense; + const hasAuditLogPermission = usePermission('can-audit-log') && hasAuditLicense; -export const useAuditItems = ({ showAudit, showAuditLog }: useAuditItemsProps): GenericMenuItemProps[] => { const t = useTranslation(); const auditHomeRoute = useRoute('audit-home'); const auditSettingsRoute = useRoute('audit-log'); + if (!hasAuditPermission && !hasAuditLogPermission) { + return []; + } + const auditMessageItem: GenericMenuItemProps = { id: 'messages', icon: 'document-eye', @@ -26,5 +31,5 @@ export const useAuditItems = ({ showAudit, showAuditLog }: useAuditItemsProps): onClick: () => auditSettingsRoute.push(), }; - return [...(showAudit ? [auditMessageItem] : []), ...(showAuditLog ? [auditLogItem] : [])]; + return [hasAuditPermission && auditMessageItem, hasAuditLogPermission && auditLogItem].filter(Boolean) as GenericMenuItemProps[]; }; diff --git a/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx index 8621846456f1..ca45e53bf5e2 100644 --- a/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx +++ b/apps/meteor/client/sidebar/header/hooks/useStatusItems.tsx @@ -2,10 +2,9 @@ import type { IUser, ValueOf } from '@rocket.chat/core-typings'; import { UserStatus as UserStatusEnum } from '@rocket.chat/core-typings'; import { Box } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import { useEndpoint, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; -import { AccountBox } from '../../../../app/ui-utils/client'; import { userStatus } from '../../../../app/user-status/client'; import { callbacks } from '../../../../lib/callbacks'; import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem'; @@ -27,9 +26,10 @@ const translateStatusName = (t: ReturnType<typeof useTranslation>, status: (type export const useStatusItems = (user: IUser): GenericMenuItemProps[] => { const t = useTranslation(); const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled'); + const setStatus = useEndpoint('POST', '/v1/users.setStatus'); - const setStatus = (status: (typeof userStatus.list)['']): void => { - AccountBox.setStatus(status.statusType, !isDefaultStatus(status.id) ? status.name : ''); + const setStatusAction = (status: (typeof userStatus.list)['']): void => { + setStatus({ status: status.statusType, message: !isDefaultStatus(status.id) ? status.name : '' }); void callbacks.run('userStatusManuallySet', status); }; @@ -64,7 +64,7 @@ export const useStatusItems = (user: IUser): GenericMenuItemProps[] => { id: status.id, status: <UserStatus status={modifier} />, content: <MarkdownText content={name} parseEmoji={true} variant='inline' />, - onClick: () => setStatus(status), + onClick: () => setStatusAction(status), disabled: presenceDisabled, }; }); diff --git a/apps/meteor/client/startup/iframeCommands.ts b/apps/meteor/client/startup/iframeCommands.ts index 3ecf578587bb..25836a38f5ca 100644 --- a/apps/meteor/client/startup/iframeCommands.ts +++ b/apps/meteor/client/startup/iframeCommands.ts @@ -5,7 +5,7 @@ import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; import { settings } from '../../app/settings/client'; -import { AccountBox } from '../../app/ui-utils/client'; +import { AccountBox } from '../../app/ui-utils/client/lib/AccountBox'; import { sdk } from '../../app/utils/client/lib/SDKClient'; import { callbacks } from '../../lib/callbacks'; import { capitalize, ltrim, rtrim } from '../../lib/utils/stringUtils'; From 1246a21648a22e03665270dbf7b6d6b2eed25f50 Mon Sep 17 00:00:00 2001 From: csuadev <72958726+csuadev@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:11:03 -0500 Subject: [PATCH 110/149] feat: Add missing variants to UIKit button (#29654) Co-authored-by: Tiago Evangelista Pinto <17487063+tiagoevanp@users.noreply.github.com> --- .changeset/cuddly-ties-bake.md | 6 ++ .../src/elements/ButtonElement.tsx | 37 +++++---- .../src/Payload/BlocksTree.ts | 35 ++++++++ .../src/Payload/action/button.ts | 81 +++++++++++++++++++ .../src/Payload/section/button.ts | 67 +++++++++++++++ 5 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 .changeset/cuddly-ties-bake.md diff --git a/.changeset/cuddly-ties-bake.md b/.changeset/cuddly-ties-bake.md new file mode 100644 index 000000000000..d912d2969d75 --- /dev/null +++ b/.changeset/cuddly-ties-bake.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/fuselage-ui-kit": minor +"@rocket.chat/uikit-playground": minor +--- + +feat: Add missing variants to UIKit button diff --git a/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx b/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx index 8f68a5bef4d6..c7fe05971e27 100644 --- a/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx +++ b/packages/fuselage-ui-kit/src/elements/ButtonElement.tsx @@ -13,28 +13,28 @@ const ButtonElement = ({ surfaceRenderer, }: ButtonElementProps): ReactElement => { const [{ loading }, action] = useUiKitState(block, context); + const { style, url, text, value, secondary } = block; - if (block.url) { + if (url) { return ( <Button is='a' target='_blank' - href={block.url} - disabled={loading} - primary={block.style === 'primary'} - danger={block.style === 'danger'} - minWidth='4ch' small + minWidth='4ch' + disabled={loading} + href={url} + primary={style === 'primary'} + danger={style === 'danger'} + success={style === 'success'} + warning={style === 'warning'} + secondary={secondary} onClick={action} > {loading ? ( <Throbber /> ) : ( - surfaceRenderer.renderTextObject( - block.text, - 0, - UiKit.BlockContext.NONE - ) + surfaceRenderer.renderTextObject(text, 0, UiKit.BlockContext.NONE) )} </Button> ); @@ -42,18 +42,21 @@ const ButtonElement = ({ return ( <Button - disabled={loading} - primary={block.style === 'primary'} - danger={block.style === 'danger'} - minWidth='4ch' small - value={block.value} + minWidth='4ch' + disabled={loading} + primary={style === 'primary'} + danger={style === 'danger'} + success={style === 'success'} + warning={style === 'warning'} + secondary={secondary} + value={value} onClick={action} > {loading ? ( <Throbber /> ) : ( - surfaceRenderer.renderTextObject(block.text, 0, UiKit.BlockContext.NONE) + surfaceRenderer.renderTextObject(text, 0, UiKit.BlockContext.NONE) )} </Button> ); diff --git a/packages/uikit-playground/src/Payload/BlocksTree.ts b/packages/uikit-playground/src/Payload/BlocksTree.ts index 67843793790b..1b561d5448d8 100644 --- a/packages/uikit-playground/src/Payload/BlocksTree.ts +++ b/packages/uikit-playground/src/Payload/BlocksTree.ts @@ -2,8 +2,12 @@ import type { Item } from '../Components/DropDown/types'; import { actionWithButtonDefault, actionWithButtonPrimary, + actionWithButtonSecondary, actionWithButtonDanger, actionWithButtonAsLink, + actionWithButtonWarning, + actionWithButtonSuccess, + actionWithButtonSecondaryWithVariant, actionWithMenu, // actionWithImage, // actionWithSingleLineInput, @@ -42,6 +46,9 @@ import { sectionWithButtonDefault, sectionWithButtonPrimary, sectionWithButtonDanger, + sectionWithButtonWarning, + sectionWithButtonSuccess, + sectionWithButtonSecondaryWithVariant, sectionWithButtonAsLink, sectionWithImage, sectionWithMenu, @@ -63,10 +70,26 @@ const BlocksTree: Item = [ label: 'primary', payload: actionWithButtonPrimary, }, + { + label: 'secondary', + payload: actionWithButtonSecondary, + }, { label: 'danger', payload: actionWithButtonDanger, }, + { + label: 'warning', + payload: actionWithButtonWarning, + }, + { + label: 'success', + payload: actionWithButtonSuccess, + }, + { + label: 'secondary with variant', + payload: actionWithButtonSecondaryWithVariant, + }, { label: 'as Link', payload: actionWithButtonAsLink, @@ -136,6 +159,18 @@ const BlocksTree: Item = [ label: 'danger', payload: sectionWithButtonDanger, }, + { + label: 'warning', + payload: sectionWithButtonWarning, + }, + { + label: 'success', + payload: sectionWithButtonSuccess, + }, + { + label: 'secondary with variant', + payload: sectionWithButtonSecondaryWithVariant, + }, { label: 'as Link', payload: sectionWithButtonAsLink, diff --git a/packages/uikit-playground/src/Payload/action/button.ts b/packages/uikit-playground/src/Payload/action/button.ts index b4e79b740008..cc54168bb1f2 100644 --- a/packages/uikit-playground/src/Payload/action/button.ts +++ b/packages/uikit-playground/src/Payload/action/button.ts @@ -39,6 +39,26 @@ export const actionWithButtonPrimary: readonly LayoutBlock[] = [ }, ]; +export const actionWithButtonSecondary: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + secondary: true, + }, + ], + }, +]; + export const actionWithButtonDanger: readonly LayoutBlock[] = [ { type: 'actions', @@ -59,6 +79,67 @@ export const actionWithButtonDanger: readonly LayoutBlock[] = [ }, ]; +export const actionWithButtonSuccess: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'success', + }, + ], + }, +]; + +export const actionWithButtonWarning: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'warning', + }, + ], + }, +]; + +export const actionWithButtonSecondaryWithVariant: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'danger', + secondary: true, + }, + ], + }, +]; + export const actionWithButtonAsLink: readonly LayoutBlock[] = [ { type: 'actions', diff --git a/packages/uikit-playground/src/Payload/section/button.ts b/packages/uikit-playground/src/Payload/section/button.ts index 63ed1140e648..f3538ca40780 100644 --- a/packages/uikit-playground/src/Payload/section/button.ts +++ b/packages/uikit-playground/src/Payload/section/button.ts @@ -65,6 +65,73 @@ export const sectionWithButtonDanger: readonly LayoutBlock[] = [ }, ]; +export const sectionWithButtonSuccess: readonly LayoutBlock[] = [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: 'This is a section block with a button.', + }, + accessory: { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'success', + }, + }, +]; + +export const sectionWithButtonWarning: readonly LayoutBlock[] = [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: 'This is a section block with a button.', + }, + accessory: { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'warning', + }, + }, +]; + +export const sectionWithButtonSecondaryWithVariant: readonly LayoutBlock[] = [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: 'This is a section block with a button.', + }, + accessory: { + type: 'button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + text: { + type: 'plain_text', + text: 'Click Me', + emoji: true, + }, + style: 'danger', + secondary: true, + }, + }, +]; + export const sectionWithButtonAsLink: readonly LayoutBlock[] = [ { type: 'section', From 52a1aa94eb94128cd02a950d73f17b250c24ab20 Mon Sep 17 00:00:00 2001 From: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> Date: Thu, 13 Jul 2023 12:55:31 +0530 Subject: [PATCH 111/149] chore: Improve system messages for omni-visitor abandonment feature (#29724) Co-authored-by: Kevin Aleman <11577696+KevLehman@users.noreply.github.com> --- .changeset/hip-mugs-promise.md | 5 ++ .../server/lib/VisitorInactivityMonitor.ts | 60 ++++++++++++------- .../rocketchat-i18n/i18n/en.i18n.json | 3 +- 3 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 .changeset/hip-mugs-promise.md diff --git a/.changeset/hip-mugs-promise.md b/.changeset/hip-mugs-promise.md new file mode 100644 index 000000000000..7100fec026e3 --- /dev/null +++ b/.changeset/hip-mugs-promise.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +improve: System messages for omni-visitor abandonment feature diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts index 845b52be2d16..e4650fc21f3d 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts @@ -1,4 +1,4 @@ -import type { IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, LivechatDepartment, Users } from '@rocket.chat/models'; import { OmnichannelEEService } from '@rocket.chat/core-services'; import { cronJobs } from '@rocket.chat/cron'; @@ -70,14 +70,13 @@ export class VisitorInactivityMonitor { _initializeMessageCache() { this.messageCache.clear(); - this.messageCache.set('default', settings.get('Livechat_abandoned_rooms_closed_custom_message') || i18n.t('Closed_automatically')); } async _getDepartmentAbandonedCustomMessage(departmentId: string) { this.logger.debug(`Getting department abandoned custom message for department ${departmentId}`); - if (this.messageCache.has('departmentId')) { + if (this.messageCache.has(departmentId)) { this.logger.debug(`Using cached department abandoned custom message for department ${departmentId}`); - return this.messageCache.get('departmentId'); + return this.messageCache.get(departmentId); } const department = await LivechatDepartment.findOneById(departmentId); if (!department) { @@ -91,7 +90,7 @@ export class VisitorInactivityMonitor { async closeRooms(room: IOmnichannelRoom) { this.logger.debug(`Closing room ${room._id}`); - let comment = this.messageCache.get('default'); + let comment = await this.getDefaultAbandonedCustomMessage('close', room.v._id); if (room.departmentId) { comment = (await this._getDepartmentAbandonedCustomMessage(room.departmentId)) || comment; } @@ -105,22 +104,8 @@ export class VisitorInactivityMonitor { async placeRoomOnHold(room: IOmnichannelRoom) { this.logger.debug(`Placing room ${room._id} on hold`); - const timeout = settings.get<number>('Livechat_visitor_inactivity_timeout'); - const { v: { _id: visitorId } = {} } = room; - if (!visitorId) { - this.logger.debug(`Room ${room._id} does not have a visitor`); - throw new Error('error-invalid_visitor'); - } - - const visitor = await LivechatVisitors.findOneById(visitorId); - if (!visitor) { - this.logger.debug(`Room ${room._id} does not have a visitor`); - throw new Error('error-invalid_visitor'); - } - - const guest = visitor.name || visitor.username; - const comment = i18n.t('Omnichannel_On_Hold_due_to_inactivity', { guest, timeout }); + const comment = await this.getDefaultAbandonedCustomMessage('on-hold', room.v._id); const result = await Promise.allSettled([ OmnichannelEEService.placeRoomOnHold(room, comment, this.user), @@ -170,4 +155,39 @@ export class VisitorInactivityMonitor { this._initializeMessageCache(); } + + private async getDefaultAbandonedCustomMessage(abandonmentAction: 'close' | 'on-hold', visitorId: string) { + const visitor = await LivechatVisitors.findOneById<Pick<ILivechatVisitor, 'name' | 'username'>>(visitorId, { + projection: { + name: 1, + username: 1, + }, + }); + if (!visitor) { + this.logger.error({ + msg: 'Error getting default abandoned custom message: visitor not found', + visitorId, + }); + throw new Error('error-invalid_visitor'); + } + + const timeout = settings.get<number>('Livechat_visitor_inactivity_timeout'); + + const guest = visitor.name || visitor.username; + + if (abandonmentAction === 'on-hold') { + return i18n.t('Omnichannel_On_Hold_due_to_inactivity', { + guest, + timeout, + }); + } + + return ( + settings.get<string>('Livechat_abandoned_rooms_closed_custom_message') || + i18n.t('Omnichannel_chat_closed_due_to_inactivity', { + guest, + timeout, + }) + ); + } } diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 6fe395013797..9f313e58c564 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3083,6 +3083,7 @@ "Livechat_offline": "Omnichannel offline", "Livechat_offline_message_sent": "Livechat offline message sent", "Livechat_OfflineMessageToChannel_enabled": "Send Livechat offline messages to a channel", + "Omnichannel_chat_closed_due_to_inactivity": "The chat was automatically closed because we haven't received any reply from {{guest}} in {{timeout}} seconds", "Omnichannel_on_hold_chat_resumed": "On Hold Chat Resumed: {{comment}}", "Omnichannel_on_hold_chat_automatically": "The chat was automatically resumed from On Hold upon receiving a new message from {{guest}}", "Omnichannel_on_hold_chat_resumed_manually": "The chat was manually resumed from On Hold by {{user}}", @@ -5947,4 +5948,4 @@ "Uninstall_grandfathered_app": "Uninstall {{appName}}?", "App_will_lose_grandfathered_status": "**This {{context}} app will lose its grandfathered status.** \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. Grandfathered apps count towards the limit but the limit is not applied to them.", "Theme_Appearence": "Theme Appearence" -} \ No newline at end of file +} From f945af67eb38aca9a116450bed39091acbc54229 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Thu, 13 Jul 2023 13:38:53 -0300 Subject: [PATCH 112/149] feat: `Navbar lvl 1` (#29671) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- .../components/Navbar/Navbar.stories.tsx | 13 +++ .../client/components/Navbar/Navbar.tsx | 13 +++ .../client/components/Navbar/NavbarAction.tsx | 10 +++ .../client/components/Navbar/NavbarBadge.tsx | 11 +++ apps/meteor/client/components/Navbar/index.ts | 3 + apps/meteor/client/navbar/Navbar.tsx | 22 +++++ .../actions/NavbarAdministrationAction.tsx | 34 ++++++++ .../navbar/actions/NavbarAuditAction.tsx | 35 ++++++++ .../navbar/actions/NavbarHomeAction.tsx | 35 ++++++++ .../actions/NavbarMarketplaceAction.tsx | 45 ++++++++++ .../navbar/actions/NavbarUserAction.tsx | 20 +++++ apps/meteor/client/navbar/index.ts | 1 + apps/meteor/client/sidebar/header/Header.tsx | 46 +++++++++++ .../client/sidebar/header/HeaderUnstable.tsx | 33 ++++++++ .../sidebar/header/UserAvatarWithStatus.tsx | 6 ++ .../header/UserAvatarWithStatusUnstable.tsx | 3 + .../meteor/client/sidebar/header/UserMenu.tsx | 36 ++++++-- .../actions/hooks/useAdministrationItems.tsx | 8 +- .../header/actions/hooks/useAppsItems.tsx | 6 ++ .../header/actions/hooks/useAuditItems.tsx | 6 ++ apps/meteor/client/sidebar/header/index.tsx | 49 ++++------- .../root/MainLayout/LayoutWithSidebar.tsx | 16 +++- apps/meteor/package.json | 1 + .../rocketchat-i18n/i18n/en.i18n.json | 2 + packages/ui-client/src/components/index.ts | 1 + .../src/hooks/useFeaturePreviewList.ts | 10 ++- yarn.lock | 82 +++++++++++++++++++ 27 files changed, 500 insertions(+), 47 deletions(-) create mode 100644 apps/meteor/client/components/Navbar/Navbar.stories.tsx create mode 100644 apps/meteor/client/components/Navbar/Navbar.tsx create mode 100644 apps/meteor/client/components/Navbar/NavbarAction.tsx create mode 100644 apps/meteor/client/components/Navbar/NavbarBadge.tsx create mode 100644 apps/meteor/client/components/Navbar/index.ts create mode 100644 apps/meteor/client/navbar/Navbar.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarAuditAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarHomeAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx create mode 100644 apps/meteor/client/navbar/actions/NavbarUserAction.tsx create mode 100644 apps/meteor/client/navbar/index.ts create mode 100644 apps/meteor/client/sidebar/header/Header.tsx create mode 100644 apps/meteor/client/sidebar/header/HeaderUnstable.tsx create mode 100644 apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx diff --git a/apps/meteor/client/components/Navbar/Navbar.stories.tsx b/apps/meteor/client/components/Navbar/Navbar.stories.tsx new file mode 100644 index 000000000000..a08e88a784a7 --- /dev/null +++ b/apps/meteor/client/components/Navbar/Navbar.stories.tsx @@ -0,0 +1,13 @@ +import { Box } from '@rocket.chat/fuselage'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import React from 'react'; + +import { Navbar } from './Navbar'; + +export default { + title: 'Components/Navbar', + component: Box, + decorators: [(story) => <Box display='flex'>{story()}</Box>], +} as ComponentMeta<typeof Box>; + +export const Default: ComponentStory<typeof Box> = (_args) => <Navbar />; diff --git a/apps/meteor/client/components/Navbar/Navbar.tsx b/apps/meteor/client/components/Navbar/Navbar.tsx new file mode 100644 index 000000000000..22b9329e3f06 --- /dev/null +++ b/apps/meteor/client/components/Navbar/Navbar.tsx @@ -0,0 +1,13 @@ +import { Box, ButtonGroup } from '@rocket.chat/fuselage'; +import type { FC } from 'react'; +import React from 'react'; + +export const Navbar: FC = ({ children }) => { + return ( + <Box aria-label='main-navigation' bg='surface-tint' is='nav' pb='x16' pi='x14'> + <ButtonGroup large role='menubar' is='ul' vertical> + {children} + </ButtonGroup> + </Box> + ); +}; diff --git a/apps/meteor/client/components/Navbar/NavbarAction.tsx b/apps/meteor/client/components/Navbar/NavbarAction.tsx new file mode 100644 index 000000000000..470f754d861a --- /dev/null +++ b/apps/meteor/client/components/Navbar/NavbarAction.tsx @@ -0,0 +1,10 @@ +import type { FC } from 'react'; +import React from 'react'; + +export const NavbarAction: FC = ({ children, ...props }) => { + return ( + <li style={{ position: 'relative' }} role='menuitem' {...props}> + {children} + </li> + ); +}; diff --git a/apps/meteor/client/components/Navbar/NavbarBadge.tsx b/apps/meteor/client/components/Navbar/NavbarBadge.tsx new file mode 100644 index 000000000000..2ab490ca0c77 --- /dev/null +++ b/apps/meteor/client/components/Navbar/NavbarBadge.tsx @@ -0,0 +1,11 @@ +import { Badge } from '@rocket.chat/fuselage'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +export const NavbarBadge = (props: Omit<AllHTMLAttributes<HTMLSpanElement>, 'is'>) => { + return ( + <div style={{ top: -5, right: -5, position: 'absolute' }}> + <Badge {...props} /> + </div> + ); +}; diff --git a/apps/meteor/client/components/Navbar/index.ts b/apps/meteor/client/components/Navbar/index.ts new file mode 100644 index 000000000000..7e752b2a8844 --- /dev/null +++ b/apps/meteor/client/components/Navbar/index.ts @@ -0,0 +1,3 @@ +export * from './Navbar'; +export * from './NavbarAction'; +export * from './NavbarBadge'; diff --git a/apps/meteor/client/navbar/Navbar.tsx b/apps/meteor/client/navbar/Navbar.tsx new file mode 100644 index 000000000000..36f82be603f2 --- /dev/null +++ b/apps/meteor/client/navbar/Navbar.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import { Navbar as NavbarComponent } from '../components/Navbar'; +import NavbarAdministrationAction from './actions/NavbarAdministrationAction'; +import NavbarAuditAction from './actions/NavbarAuditAction'; +import NavbarHomeAction from './actions/NavbarHomeAction'; +import NavbarMarketplaceAction from './actions/NavbarMarketplaceAction'; +import NavbarUserAction from './actions/NavbarUserAction'; + +const Navbar = () => { + return ( + <NavbarComponent> + <NavbarUserAction /> + <NavbarHomeAction /> + <NavbarMarketplaceAction /> + <NavbarAuditAction /> + <NavbarAdministrationAction /> + </NavbarComponent> + ); +}; + +export default Navbar; diff --git a/apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx b/apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx new file mode 100644 index 000000000000..3431b9e928ad --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarAdministrationAction.tsx @@ -0,0 +1,34 @@ +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import GenericMenu from '../../components/GenericMenu/GenericMenu'; +import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; +import { NavbarAction } from '../../components/Navbar'; +import { useAdministrationItems } from '../../sidebar/header/actions/hooks/useAdministrationItems'; + +const NavbarAdministrationAction = (props: AllHTMLAttributes<HTMLLIElement>) => { + const t = useTranslation(); + + const administrationItems = useAdministrationItems(); + + const handleAction = useHandleMenuAction(administrationItems); + + const router = useRouter(); + + return ( + <NavbarAction {...props}> + <GenericMenu + pressed={router.getLocationPathname().startsWith('/admin')} + medium + title={t('Administration')} + icon='cog' + onAction={handleAction} + items={administrationItems} + placement='right-start' + /> + </NavbarAction> + ); +}; + +export default NavbarAdministrationAction; diff --git a/apps/meteor/client/navbar/actions/NavbarAuditAction.tsx b/apps/meteor/client/navbar/actions/NavbarAuditAction.tsx new file mode 100644 index 000000000000..fc613be29b99 --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarAuditAction.tsx @@ -0,0 +1,35 @@ +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import GenericMenu from '../../components/GenericMenu/GenericMenu'; +import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; +import { NavbarAction } from '../../components/Navbar'; +import { useAuditItems } from '../../sidebar/header/actions/hooks/useAuditItems'; + +const NavbarAuditAction = (props: AllHTMLAttributes<HTMLLIElement>) => { + const t = useTranslation(); + + const router = useRouter(); + const routerName = router.getRouteName(); + + const auditItems = useAuditItems(); + + const handleAction = useHandleMenuAction(auditItems); + + return ( + <NavbarAction {...props}> + <GenericMenu + pressed={routerName === 'audit-home' || routerName === 'audit-log'} + medium + title={t('Audit')} + icon='document-eye' + placement='right-start' + onAction={handleAction} + items={auditItems} + /> + </NavbarAction> + ); +}; + +export default NavbarAuditAction; diff --git a/apps/meteor/client/navbar/actions/NavbarHomeAction.tsx b/apps/meteor/client/navbar/actions/NavbarHomeAction.tsx new file mode 100644 index 000000000000..accd68817de9 --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarHomeAction.tsx @@ -0,0 +1,35 @@ +import { IconButton } from '@rocket.chat/fuselage'; +import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import { useRouter, useLayout, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import type { HTMLAttributes, VFC } from 'react'; +import React from 'react'; + +import { NavbarAction } from '../../components/Navbar'; + +const NavbarHomeAction: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (props) => { + const t = useTranslation(); + const router = useRouter(); + const { sidebar } = useLayout(); + const showHome = useSetting('Layout_Show_Home_Button'); + + const routeName = router.getLocationPathname(); + + const handleHome = useMutableCallback(() => { + sidebar.toggle(); + router.navigate('/home'); + }); + + return showHome ? ( + <NavbarAction {...props}> + <IconButton + pressed={['/home', '/live', '/direct', '/group', '/channel'].some((name) => routeName?.startsWith(name))} + title={t('Home')} + medium + icon='home' + onClick={handleHome} + /> + </NavbarAction> + ) : null; +}; + +export default NavbarHomeAction; diff --git a/apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx b/apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx new file mode 100644 index 000000000000..8f54b8260b2b --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarMarketplaceAction.tsx @@ -0,0 +1,45 @@ +import { IconButton } from '@rocket.chat/fuselage'; +import { useTranslation, useRouter } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import GenericMenu from '../../components/GenericMenu/GenericMenu'; +import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; +import { NavbarAction } from '../../components/Navbar'; +import { useAppsItems } from '../../sidebar/header/actions/hooks/useAppsItems'; + +const NavbarMarketplaceAction = (props: AllHTMLAttributes<HTMLLIElement>) => { + const t = useTranslation(); + const router = useRouter(); + const routeName = router.getRouteName(); + + const appItems = useAppsItems(); + + const handleAction = useHandleMenuAction(appItems); + + const showApps = appItems.length > 0; + + if (!showApps) { + return ( + <NavbarAction {...props}> + <IconButton icon='store' disabled /> + </NavbarAction> + ); + } + + return ( + <NavbarAction {...props}> + <GenericMenu + pressed={routeName === 'marketplace'} + medium + title={t('Marketplace')} + icon='store' + onAction={handleAction} + items={appItems} + placement='right-start' + /> + </NavbarAction> + ); +}; + +export default NavbarMarketplaceAction; diff --git a/apps/meteor/client/navbar/actions/NavbarUserAction.tsx b/apps/meteor/client/navbar/actions/NavbarUserAction.tsx new file mode 100644 index 000000000000..3b0a6cf99578 --- /dev/null +++ b/apps/meteor/client/navbar/actions/NavbarUserAction.tsx @@ -0,0 +1,20 @@ +import { Margins } from '@rocket.chat/fuselage'; +import { useUser } from '@rocket.chat/ui-contexts'; +import type { AllHTMLAttributes } from 'react'; +import React from 'react'; + +import { NavbarAction } from '../../components/Navbar'; +import UserAvatarWithStatusUnstable from '../../sidebar/header/UserAvatarWithStatusUnstable'; +import UserMenu from '../../sidebar/header/UserMenu'; + +const NavbarUserAction = (props: AllHTMLAttributes<HTMLLIElement>) => { + const user = useUser(); + + return ( + <NavbarAction {...props}> + <Margins blockEnd='x16'>{user ? <UserMenu user={user} /> : <UserAvatarWithStatusUnstable />}</Margins> + </NavbarAction> + ); +}; + +export default NavbarUserAction; diff --git a/apps/meteor/client/navbar/index.ts b/apps/meteor/client/navbar/index.ts new file mode 100644 index 000000000000..86d1b79b3ea2 --- /dev/null +++ b/apps/meteor/client/navbar/index.ts @@ -0,0 +1 @@ +export { default } from './Navbar'; diff --git a/apps/meteor/client/sidebar/header/Header.tsx b/apps/meteor/client/sidebar/header/Header.tsx new file mode 100644 index 000000000000..8b1c838f8d62 --- /dev/null +++ b/apps/meteor/client/sidebar/header/Header.tsx @@ -0,0 +1,46 @@ +import { Sidebar } from '@rocket.chat/fuselage'; +import { useUser, useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import React, { memo } from 'react'; + +import UserAvatarWithStatus from './UserAvatarWithStatus'; +import UserMenu from './UserMenu'; +import Administration from './actions/Administration'; +import CreateRoom from './actions/CreateRoom'; +import Directory from './actions/Directory'; +import Home from './actions/Home'; +import Login from './actions/Login'; +import Search from './actions/Search'; +import Sort from './actions/Sort'; + +/** + * @deprecated Feature preview + * @description Should be removed when the feature became part of the core + * @memberof navigationBar + */ + +const Header = (): ReactElement => { + const t = useTranslation(); + const user = useUser(); + + return ( + <Sidebar.TopBar.Section> + {user ? <UserMenu user={user} /> : <UserAvatarWithStatus />} + <Sidebar.TopBar.Actions> + <Home title={t('Home')} /> + <Search title={t('Search')} /> + {user && ( + <> + <Directory title={t('Directory')} /> + <Sort title={t('Display')} /> + <CreateRoom title={t('Create_new')} data-qa='sidebar-create' /> + <Administration title={t('Administration')} /> + </> + )} + {!user && <Login title={t('Login')} />} + </Sidebar.TopBar.Actions> + </Sidebar.TopBar.Section> + ); +}; + +export default memo(Header); diff --git a/apps/meteor/client/sidebar/header/HeaderUnstable.tsx b/apps/meteor/client/sidebar/header/HeaderUnstable.tsx new file mode 100644 index 000000000000..319d3fb25e16 --- /dev/null +++ b/apps/meteor/client/sidebar/header/HeaderUnstable.tsx @@ -0,0 +1,33 @@ +import { Sidebar } from '@rocket.chat/fuselage'; +import { useUserId, useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactElement } from 'react'; +import React, { memo } from 'react'; + +import CreateRoom from './actions/CreateRoom'; +import Directory from './actions/Directory'; +import Login from './actions/Login'; +import Search from './actions/Search'; +import Sort from './actions/Sort'; + +const HeaderUnstable = (): ReactElement => { + const t = useTranslation(); + const uid = useUserId(); + + return ( + <Sidebar.TopBar.Section> + <Sidebar.TopBar.Actions justifyContent='end' width='100%'> + <Search title={t('Search')} /> + {uid && ( + <> + <Directory title={t('Directory')} /> + <Sort title={t('Display')} /> + <CreateRoom title={t('Create_new')} data-qa='sidebar-create' /> + </> + )} + {!uid && <Login title={t('Login')} />} + </Sidebar.TopBar.Actions> + </Sidebar.TopBar.Section> + ); +}; + +export default memo(HeaderUnstable); diff --git a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx index f1abaa6a6269..4a9f4be60408 100644 --- a/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx +++ b/apps/meteor/client/sidebar/header/UserAvatarWithStatus.tsx @@ -14,6 +14,12 @@ const anon = { avatarETag: undefined, } as const; +/** + * @deprecated Feature preview + * @description Should be moved to the core when the feature is ready + * @memberof navigationBar + */ + const UserAvatarWithStatus = () => { const user = useUser(); const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled'); diff --git a/apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx b/apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx new file mode 100644 index 000000000000..229985610da3 --- /dev/null +++ b/apps/meteor/client/sidebar/header/UserAvatarWithStatusUnstable.tsx @@ -0,0 +1,3 @@ +import UserAvatarWithStatus from './UserAvatarWithStatus'; + +export default UserAvatarWithStatus; diff --git a/apps/meteor/client/sidebar/header/UserMenu.tsx b/apps/meteor/client/sidebar/header/UserMenu.tsx index 6347f6fad92d..0b44c09daff5 100644 --- a/apps/meteor/client/sidebar/header/UserMenu.tsx +++ b/apps/meteor/client/sidebar/header/UserMenu.tsx @@ -1,10 +1,12 @@ import type { IUser } from '@rocket.chat/core-typings'; +import { FeaturePreview, FeaturePreviewOn, FeaturePreviewOff } from '@rocket.chat/ui-client'; import React, { useState, memo } from 'react'; import GenericMenu from '../../components/GenericMenu/GenericMenu'; import type { GenericMenuItemProps } from '../../components/GenericMenu/GenericMenuItem'; import { useHandleMenuAction } from '../../components/GenericMenu/hooks/useHandleMenuAction'; import UserAvatarWithStatus from './UserAvatarWithStatus'; +import UserAvatarWithStatusUnstable from './UserAvatarWithStatusUnstable'; import { useUserMenu } from './hooks/useUserMenu'; const UserMenu = ({ user }: { user: IUser }) => { @@ -16,15 +18,31 @@ const UserMenu = ({ user }: { user: IUser }) => { const handleAction = useHandleMenuAction(items, () => setIsOpen(false)); return ( - <GenericMenu - icon={<UserAvatarWithStatus />} - selectionMode='multiple' - sections={sections} - title='User menu' - onAction={handleAction} - isOpen={isOpen} - onOpenChange={setIsOpen} - /> + <FeaturePreview feature='navigationBar'> + <FeaturePreviewOff> + <GenericMenu + icon={<UserAvatarWithStatus />} + selectionMode='multiple' + sections={sections} + title='User menu' + onAction={handleAction} + isOpen={isOpen} + onOpenChange={setIsOpen} + /> + </FeaturePreviewOff> + <FeaturePreviewOn> + <GenericMenu + icon={<UserAvatarWithStatusUnstable />} + medium + selectionMode='multiple' + sections={sections} + title='User menu' + onAction={handleAction} + isOpen={isOpen} + onOpenChange={setIsOpen} + /> + </FeaturePreviewOn> + </FeaturePreview> ); }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index c0dc83fb62c1..3d4d640fecf2 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -46,6 +46,12 @@ const ADMIN_PERMISSIONS = [ 'view-moderation-console', ]; +/** + * @deprecated Feature preview + * @description Should be moved to navbar when the feature became part of the core + * @memberof navigationBar + */ + export const useAdministrationItems = (): GenericMenuItemProps[] => { const router = useRouter(); const t = useTranslation(); @@ -119,8 +125,8 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => { return [ showUpgradeItem && upgradeItem, + shouldShowAdminMenu && workspaceItem, isAdmin && adminItem, omnichannel && omnichannelItem, - shouldShowAdminMenu && workspaceItem, ].filter(Boolean) as GenericMenuItemProps[]; }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx index 6ecd34b62d5d..3717f33fb195 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.tsx @@ -6,6 +6,12 @@ import type { GenericMenuItemProps } from '../../../../components/GenericMenu/Ge import { useUserDropdownAppsActionButtons } from '../../../../hooks/useAppActionButtons'; import { useAppRequestStats } from '../../../../views/marketplace/hooks/useAppRequestStats'; +/** + * @deprecated Feature preview + * @description Should be moved to navbar when the feature became part of the core + * @memberof navigationBar + */ + export const useAppsItems = (): GenericMenuItemProps[] => { const t = useTranslation(); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx index 07b2b9bb11c3..f506255806bf 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.tsx @@ -3,6 +3,12 @@ import { useTranslation, useRoute, usePermission } from '@rocket.chat/ui-context import { useHasLicenseModule } from '../../../../../ee/client/hooks/useHasLicenseModule'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; +/** + * @deprecated Feature preview + * @description Should be moved to navbar when the feature became part of the core + * @memberof navigationBar + */ + export const useAuditItems = (): GenericMenuItemProps[] => { const hasAuditLicense = useHasLicenseModule('auditing') === true; diff --git a/apps/meteor/client/sidebar/header/index.tsx b/apps/meteor/client/sidebar/header/index.tsx index 95b225c0a242..6fa03502d76a 100644 --- a/apps/meteor/client/sidebar/header/index.tsx +++ b/apps/meteor/client/sidebar/header/index.tsx @@ -1,42 +1,21 @@ -import { Sidebar } from '@rocket.chat/fuselage'; -import { useUser, useTranslation } from '@rocket.chat/ui-contexts'; +import { FeaturePreview, FeaturePreviewOn, FeaturePreviewOff } from '@rocket.chat/ui-client'; import type { ReactElement } from 'react'; -import React, { memo } from 'react'; +import React, { lazy, memo } from 'react'; -import UserAvatarWithStatus from './UserAvatarWithStatus'; -import UserMenu from './UserMenu'; -import Administration from './actions/Administration'; -import CreateRoom from './actions/CreateRoom'; -import Directory from './actions/Directory'; -import Home from './actions/Home'; -import Login from './actions/Login'; -import Search from './actions/Search'; -import Sort from './actions/Sort'; - -const HeaderWithData = (): ReactElement => { - const t = useTranslation(); - const user = useUser(); +const Header = lazy(() => import('./Header')); +const HeaderUnstable = lazy(() => import('./HeaderUnstable')); +const HeaderWrapper = (): ReactElement => { return ( - <> - <Sidebar.TopBar.Section> - {user ? <UserMenu user={user} /> : <UserAvatarWithStatus />} - <Sidebar.TopBar.Actions> - <Home title={t('Home')} /> - <Search title={t('Search')} /> - {user && ( - <> - <Directory title={t('Directory')} /> - <Sort title={t('Display')} /> - <CreateRoom title={t('Create_new')} data-qa='sidebar-create' /> - <Administration title={t('Administration')} /> - </> - )} - {!user && <Login title={t('Login')} />} - </Sidebar.TopBar.Actions> - </Sidebar.TopBar.Section> - </> + <FeaturePreview feature='navigationBar'> + <FeaturePreviewOff> + <Header /> + </FeaturePreviewOff> + <FeaturePreviewOn> + <HeaderUnstable /> + </FeaturePreviewOn> + </FeaturePreview> ); }; -export default memo(HeaderWithData); +export default memo(HeaderWrapper); diff --git a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx index ee50ad826785..901ced6e7305 100644 --- a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx +++ b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx @@ -1,10 +1,12 @@ import { Box } from '@rocket.chat/fuselage'; +import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from '@rocket.chat/ui-client'; import { useLayout, useSetting, useCurrentModal, useRoute, useCurrentRoutePath } from '@rocket.chat/ui-contexts'; import { PaletteStyleTag } from '@rocket.chat/ui-theming/src/PaletteStyleTag'; import { SidebarPaletteStyleTag } from '@rocket.chat/ui-theming/src/SidebarPaletteStyleTag'; import type { ReactElement, ReactNode } from 'react'; import React, { useEffect, useRef } from 'react'; +import Navbar from '../../../navbar'; import Sidebar from '../../../sidebar'; const LayoutWithSidebar = ({ children }: { children: ReactNode }): ReactElement => { @@ -48,7 +50,19 @@ const LayoutWithSidebar = ({ children }: { children: ReactNode }): ReactElement > <PaletteStyleTag /> <SidebarPaletteStyleTag /> - {!removeSidenav ? <Sidebar /> : null} + {!removeSidenav ? ( + <> + <FeaturePreview feature='navigationBar'> + <FeaturePreviewOn> + <Navbar /> + </FeaturePreviewOn> + <FeaturePreviewOff> + <></> + </FeaturePreviewOff> + </FeaturePreview> + <Sidebar /> + </> + ) : null} <div className={['rc-old', 'main-content', readReceiptsEnabled ? 'read-receipts-enabled' : undefined].filter(Boolean).join(' ')}> {children} </div> diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 99385d0b56f3..7235923ab100 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -75,6 +75,7 @@ "@rocket.chat/livechat": "workspace:^", "@rocket.chat/mock-providers": "workspace:^", "@settlin/spacebars-loader": "^1.0.9", + "@storybook/addon-a11y": "6.5.16", "@storybook/addon-essentials": "~6.5.16", "@storybook/addon-interactions": "~6.5.16", "@storybook/addon-postcss": "~2.0.0", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 9f313e58c564..5b23d85fc71d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3569,6 +3569,8 @@ "Name_optional": "Name (optional)", "Name_Placeholder": "Please enter your name...", "Navigation": "Navigation", + "Navigation_bar": "Navigation bar", + "Navigation_bar_description": "Introducing the navigation bar — a higher-level navigation designed to help users quickly find what they need. With its compact design and intuitive organization, this streamlined sidebar optimizes screen space while providing easy access to essential software features and sections.", "Navigation_History": "Navigation History", "Next": "Next", "Never": "Never", diff --git a/packages/ui-client/src/components/index.ts b/packages/ui-client/src/components/index.ts index 5a0f1463be80..e40cfd9a5575 100644 --- a/packages/ui-client/src/components/index.ts +++ b/packages/ui-client/src/components/index.ts @@ -8,3 +8,4 @@ export * from './TooltipComponent'; export * as UserStatus from './UserStatus'; export { default as Card } from './Card'; export * from './Header'; +export * from './FeaturePreview/FeaturePreview'; diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.ts b/packages/ui-client/src/hooks/useFeaturePreviewList.ts index b4e9a1dee6d3..87a89e9d5a67 100644 --- a/packages/ui-client/src/hooks/useFeaturePreviewList.ts +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.ts @@ -1,7 +1,7 @@ import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts'; -export type FeaturesAvailable = 'quickReactions'; +export type FeaturesAvailable = 'quickReactions' | 'navigationBar'; export type FeaturePreviewProps = { name: FeaturesAvailable; @@ -21,6 +21,14 @@ export const defaultFeaturesPreview: FeaturePreviewProps[] = [ imageUrl: 'images/featurePreview/quick-reactions.png', value: false, }, + { + name: 'navigationBar', + i18n: 'Navigation_bar', + description: 'Navigation_bar_description', + group: 'Navigation', + imageUrl: 'images/featurePreview/quick-reactions.png', + value: false, + }, ]; export const useFeaturePreviewList = () => { diff --git a/yarn.lock b/yarn.lock index 4ba47faae660..a4da00ac674e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10105,6 +10105,7 @@ __metadata: "@rocket.chat/web-ui-registration": "workspace:^" "@settlin/spacebars-loader": ^1.0.9 "@slack/rtm-api": ^6.0.0 + "@storybook/addon-a11y": 6.5.16 "@storybook/addon-essentials": ~6.5.16 "@storybook/addon-interactions": ~6.5.16 "@storybook/addon-postcss": ~2.0.0 @@ -11292,6 +11293,38 @@ __metadata: languageName: node linkType: hard +"@storybook/addon-a11y@npm:6.5.16": + version: 6.5.16 + resolution: "@storybook/addon-a11y@npm:6.5.16" + dependencies: + "@storybook/addons": 6.5.16 + "@storybook/api": 6.5.16 + "@storybook/channels": 6.5.16 + "@storybook/client-logger": 6.5.16 + "@storybook/components": 6.5.16 + "@storybook/core-events": 6.5.16 + "@storybook/csf": 0.0.2--canary.4566f4d.1 + "@storybook/theming": 6.5.16 + axe-core: ^4.2.0 + core-js: ^3.8.2 + global: ^4.4.0 + lodash: ^4.17.21 + react-sizeme: ^3.0.1 + regenerator-runtime: ^0.13.7 + ts-dedent: ^2.0.0 + util-deprecate: ^1.0.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + checksum: 05ce7f696254782b521a5e946f7d58b207d854e69ce8b624a14de0192ce558f640b9dae821e122911059a24d2f9907e783e8dce5fb9288d5cfbdf0833aaab503 + languageName: node + linkType: hard + "@storybook/addon-actions@npm:6.5.16, @storybook/addon-actions@npm:~6.5.16": version: 6.5.16 resolution: "@storybook/addon-actions@npm:6.5.16" @@ -16572,6 +16605,13 @@ __metadata: languageName: node linkType: hard +"axe-core@npm:^4.2.0": + version: 4.7.2 + resolution: "axe-core@npm:4.7.2" + checksum: 5d86fa0f45213b0e54cbb5d713ce885c4a8fe3a72b92dd915a47aa396d6fd149c4a87fec53aa978511f6d941402256cfeb26f2db35129e370f25a453c688655a + languageName: node + linkType: hard + "axios@npm:^0.21.0, axios@npm:^0.21.1": version: 0.21.4 resolution: "axios@npm:0.21.4" @@ -17010,6 +17050,13 @@ __metadata: languageName: node linkType: hard +"batch-processor@npm:1.0.0": + version: 1.0.0 + resolution: "batch-processor@npm:1.0.0" + checksum: 5519b024f6cd0e95a543bb3edf0ae19e5badae0c32b30b41839b4469bbb1f91e14fc04bff3759cd9c2621aa9e16def48c938783e9027e7ec977fba62d537a468 + languageName: node + linkType: hard + "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -21278,6 +21325,15 @@ __metadata: languageName: node linkType: hard +"element-resize-detector@npm:^1.2.2": + version: 1.2.4 + resolution: "element-resize-detector@npm:1.2.4" + dependencies: + batch-processor: 1.0.0 + checksum: 81c47b7e229c303889d3a9d78ec3f3232e88a6682f1e2424fb0632d9b4f503b2ca011e6954321060604da07749a5a972b6a175fdf6c6806093a3b80a304cde7b + languageName: node + linkType: hard + "elliptic@npm:^6.5.3, elliptic@npm:^6.5.4": version: 6.5.4 resolution: "elliptic@npm:6.5.4" @@ -35116,6 +35172,18 @@ __metadata: languageName: node linkType: hard +"react-sizeme@npm:^3.0.1": + version: 3.0.2 + resolution: "react-sizeme@npm:3.0.2" + dependencies: + element-resize-detector: ^1.2.2 + invariant: ^2.2.4 + shallowequal: ^1.1.0 + throttle-debounce: ^3.0.1 + checksum: 97cb852c24bbd50acb310da89df564e0d069415f6635676dae3d3bdc583ece88090c0f2ee88a6b0dc36d2793af4a11e83bf6bbb41b86225dd0cf338e8f7e8552 + languageName: node + linkType: hard + "react-split-pane@npm:^0.1.92": version: 0.1.92 resolution: "react-split-pane@npm:0.1.92" @@ -36971,6 +37039,13 @@ __metadata: languageName: node linkType: hard +"shallowequal@npm:^1.1.0": + version: 1.1.0 + resolution: "shallowequal@npm:1.1.0" + checksum: f4c1de0837f106d2dbbfd5d0720a5d059d1c66b42b580965c8f06bb1db684be8783538b684092648c981294bf817869f743a066538771dbecb293df78f765e00 + languageName: node + linkType: hard + "sharp@npm:^0.30.7": version: 0.30.7 resolution: "sharp@npm:0.30.7" @@ -38964,6 +39039,13 @@ __metadata: languageName: node linkType: hard +"throttle-debounce@npm:^3.0.1": + version: 3.0.1 + resolution: "throttle-debounce@npm:3.0.1" + checksum: e34ef638e8df3a9154249101b68afcbf2652a139c803415ef8a2f6a8bc577bcd4d79e4bb914ad3cd206523ac78b9fb7e80885bfa049f64fbb1927f99d98b5736 + languageName: node + linkType: hard + "through2@npm:^2.0.0, through2@npm:~2.0.3": version: 2.0.5 resolution: "through2@npm:2.0.5" From 841ec6678e293f567a8351cf91a14ab165a272a8 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:13:43 +0530 Subject: [PATCH 113/149] chore(gazzodown): remove `ui-client` and `ui-contexts` peer deps from `gazzodown` (#29820) --- apps/meteor/client/components/GazzodownText.tsx | 9 +++++++-- packages/gazzodown/package.json | 4 ---- packages/gazzodown/src/MarkupInteractionContext.ts | 3 +++ packages/gazzodown/src/mentions/UserMentionElement.tsx | 9 +++------ yarn.lock | 4 ---- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/meteor/client/components/GazzodownText.tsx b/apps/meteor/client/components/GazzodownText.tsx index e9a0f39ddb5f..7912e44ad925 100644 --- a/apps/meteor/client/components/GazzodownText.tsx +++ b/apps/meteor/client/components/GazzodownText.tsx @@ -2,7 +2,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import type { ChannelMention, UserMention } from '@rocket.chat/gazzodown'; import { MarkupInteractionContext } from '@rocket.chat/gazzodown'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import { useLayout, useRouter, useUserPreference } from '@rocket.chat/ui-contexts'; +import { useLayout, useRouter, useSetting, useUserPreference, useUserId } from '@rocket.chat/ui-contexts'; import type { UIEvent } from 'react'; import React, { useCallback, memo, useMemo } from 'react'; @@ -47,6 +47,8 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe const convertAsciiToEmoji = useUserPreference<boolean>('convertAsciiEmoji', true); const useEmoji = Boolean(useUserPreference('useEmojis')); + const useRealName = Boolean(useSetting('UI_Use_Real_Name')); + const ownUserId = useUserId(); const chat = useChat(); @@ -80,7 +82,7 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe const goToRoom = useGoToRoom(); - const { isEmbedded } = useLayout(); + const { isEmbedded, isMobile } = useLayout(); const resolveChannelMention = useCallback((mention: string) => channels?.find(({ name }) => name === mention), [channels]); @@ -117,6 +119,9 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe onChannelMentionClick, convertAsciiToEmoji, useEmoji, + useRealName, + isMobile, + ownUserId, }} > {children} diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index 46b0d6396122..e7d0cdd277f2 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -10,8 +10,6 @@ "@rocket.chat/fuselage-tokens": "next", "@rocket.chat/message-parser": "next", "@rocket.chat/styled": "next", - "@rocket.chat/ui-client": "workspace:^", - "@rocket.chat/ui-contexts": "workspace:^", "@storybook/addon-actions": "~6.5.16", "@storybook/addon-docs": "~6.5.16", "@storybook/addon-essentials": "~6.5.16", @@ -71,8 +69,6 @@ "@rocket.chat/fuselage-tokens": "*", "@rocket.chat/message-parser": "*", "@rocket.chat/styled": "*", - "@rocket.chat/ui-client": "*", - "@rocket.chat/ui-contexts": "*", "katex": "*", "react": "*" }, diff --git a/packages/gazzodown/src/MarkupInteractionContext.ts b/packages/gazzodown/src/MarkupInteractionContext.ts index 1f2e3b59c9bf..eed1c2cd4236 100644 --- a/packages/gazzodown/src/MarkupInteractionContext.ts +++ b/packages/gazzodown/src/MarkupInteractionContext.ts @@ -16,6 +16,9 @@ type MarkupInteractionContextValue = { onChannelMentionClick?: (mentionedChannel: ChannelMention) => ((e: UIEvent) => void) | undefined; convertAsciiToEmoji?: boolean; useEmoji?: boolean; + useRealName?: boolean; + isMobile?: boolean; + ownUserId?: string | null; }; export const MarkupInteractionContext = createContext<MarkupInteractionContextValue>({}); diff --git a/packages/gazzodown/src/mentions/UserMentionElement.tsx b/packages/gazzodown/src/mentions/UserMentionElement.tsx index 65a238e0de1e..15b8e226af81 100644 --- a/packages/gazzodown/src/mentions/UserMentionElement.tsx +++ b/packages/gazzodown/src/mentions/UserMentionElement.tsx @@ -1,5 +1,4 @@ import { Message } from '@rocket.chat/fuselage'; -import { useLayout, useSetting, useUserId } from '@rocket.chat/ui-contexts'; import { memo, ReactElement, useContext, useMemo } from 'react'; import { MarkupInteractionContext } from '../MarkupInteractionContext'; @@ -9,14 +8,12 @@ type UserMentionElementProps = { }; const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement => { - const { resolveUserMention, onUserMentionClick } = useContext(MarkupInteractionContext); + const { resolveUserMention, onUserMentionClick, isMobile, ownUserId, useRealName } = useContext(MarkupInteractionContext); const resolved = useMemo(() => resolveUserMention?.(mention), [mention, resolveUserMention]); const handleClick = useMemo(() => (resolved ? onUserMentionClick?.(resolved) : undefined), [resolved, onUserMentionClick]); - const { isMobile } = useLayout(); - const uid = useUserId(); - const showRealName = useSetting<boolean>('UI_Use_Real_Name') && !isMobile; + const showRealName = useRealName && !isMobile; if (mention === 'all') { return <Message.Highlight variant='relevant'>all</Message.Highlight>; @@ -32,7 +29,7 @@ const UserMentionElement = ({ mention }: UserMentionElementProps): ReactElement return ( <Message.Highlight - variant={resolved._id === uid ? 'critical' : 'other'} + variant={resolved._id === ownUserId ? 'critical' : 'other'} title={resolved.username || resolved.name} clickable onClick={handleClick} diff --git a/yarn.lock b/yarn.lock index a4da00ac674e..f9b00db29e95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9789,8 +9789,6 @@ __metadata: "@rocket.chat/fuselage-tokens": next "@rocket.chat/message-parser": next "@rocket.chat/styled": next - "@rocket.chat/ui-client": "workspace:^" - "@rocket.chat/ui-contexts": "workspace:^" "@storybook/addon-actions": ~6.5.16 "@storybook/addon-docs": ~6.5.16 "@storybook/addon-essentials": ~6.5.16 @@ -9835,8 +9833,6 @@ __metadata: "@rocket.chat/fuselage-tokens": "*" "@rocket.chat/message-parser": "*" "@rocket.chat/styled": "*" - "@rocket.chat/ui-client": "*" - "@rocket.chat/ui-contexts": "*" katex: "*" react: "*" languageName: unknown From 5dac62ced6440a48edc5299a53bf1e10ca26ca6d Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Fri, 14 Jul 2023 11:54:54 -0300 Subject: [PATCH 114/149] regression: Apply right filters to action buttons and convert `applyButtonFilters` to `useApplyButtonFilters` (#29822) --- apps/meteor/.mocharc.client.js | 2 +- .../actionButtons/lib/applyButtonFilters.ts | 58 --- .../client/hooks/useAppActionButtons.ts | 25 +- .../client/hooks/useApplyButtonFilters.ts | 65 +++ .../providers/AuthorizationProvider.tsx | 5 +- .../actions/hooks/useAppsItems.spec.tsx | 488 ++++++++++++++++++ apps/meteor/jest.client.config.ts | 6 +- .../src/MockedAuthorizationContext.tsx | 19 +- .../src/MockedServerContext.tsx | 1 + .../ui-contexts/src/AuthorizationContext.ts | 8 +- 10 files changed, 599 insertions(+), 78 deletions(-) delete mode 100644 apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts create mode 100644 apps/meteor/client/hooks/useApplyButtonFilters.ts create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 7eff846f683c..6e522ee67363 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -38,5 +38,5 @@ module.exports = { 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts', ], - exclude: ['client/hooks/*.spec.{ts,tsx}'], + exclude: ['client/hooks/*.spec.{ts,tsx}', 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}'], }; diff --git a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts b/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts deleted file mode 100644 index be38eaa6a22c..000000000000 --- a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* Style disabled as having some arrow functions in one-line hurts readability */ -/* eslint-disable arrow-body-style */ - -import { Meteor } from 'meteor/meteor'; -import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; -import { RoomTypeFilter } from '@rocket.chat/apps-engine/definition/ui'; -import type { IRoom } from '@rocket.chat/core-typings'; -import { - isDirectMessageRoom, - isMultipleDirectMessageRoom, - isOmnichannelRoom, - isPrivateDiscussion, - isPrivateTeamRoom, - isPublicDiscussion, - isPublicTeamRoom, -} from '@rocket.chat/core-typings'; - -import { hasAtLeastOnePermission, hasPermission, hasRole, hasAnyRole } from '../../../../authorization/client'; - -const applyAuthFilter = (button: IUIActionButton, room?: IRoom, ignoreSubscriptions = false): boolean => { - const { hasAllPermissions, hasOnePermission, hasAllRoles, hasOneRole } = button.when || {}; - - const userId = Meteor.userId(); - - const hasAllPermissionsResult = hasAllPermissions ? hasPermission(hasAllPermissions) : true; - const hasOnePermissionResult = hasOnePermission ? hasAtLeastOnePermission(hasOnePermission) : true; - const hasAllRolesResult = hasAllRoles - ? !!userId && hasAllRoles.every((role) => hasRole(userId, role, room?._id, ignoreSubscriptions)) - : true; - const hasOneRoleResult = hasOneRole ? !!userId && hasAnyRole(userId, hasOneRole, room?._id, ignoreSubscriptions) : true; - - return hasAllPermissionsResult && hasOnePermissionResult && hasAllRolesResult && hasOneRoleResult; -}; - -const enumToFilter: { [k in RoomTypeFilter]: (room: IRoom) => boolean } = { - [RoomTypeFilter.PUBLIC_CHANNEL]: (room) => room.t === 'c', - [RoomTypeFilter.PRIVATE_CHANNEL]: (room) => room.t === 'p', - [RoomTypeFilter.PUBLIC_TEAM]: isPublicTeamRoom, - [RoomTypeFilter.PRIVATE_TEAM]: isPrivateTeamRoom, - [RoomTypeFilter.PUBLIC_DISCUSSION]: isPublicDiscussion, - [RoomTypeFilter.PRIVATE_DISCUSSION]: isPrivateDiscussion, - [RoomTypeFilter.DIRECT]: isDirectMessageRoom, - [RoomTypeFilter.DIRECT_MULTIPLE]: isMultipleDirectMessageRoom, - [RoomTypeFilter.LIVE_CHAT]: isOmnichannelRoom, -}; - -const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => { - const { roomTypes } = button.when || {}; - return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room)); -}; - -export const applyButtonFilters = (button: IUIActionButton, room?: IRoom): boolean => { - return applyAuthFilter(button, room) && (!room || applyRoomFilter(button, room)); -}; - -export const applyDropdownActionButtonFilters = (button: IUIActionButton): boolean => { - return applyAuthFilter(button, undefined, true); -}; diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts index 1f539c49b002..6d5aac3e92a3 100644 --- a/apps/meteor/client/hooks/useAppActionButtons.ts +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -4,13 +4,13 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useEffect, useRef, useMemo } from 'react'; -import { applyButtonFilters } from '../../app/ui-message/client/actionButtons/lib/applyButtonFilters'; import type { MessageActionConfig, MessageActionContext } from '../../app/ui-utils/client/lib/MessageAction'; import type { MessageBoxAction } from '../../app/ui-utils/client/lib/messageBox'; import { Utilities } from '../../ee/lib/misc/Utilities'; import type { GenericMenuItemProps } from '../components/GenericMenu/GenericMenuItem'; import { useRoom } from '../views/room/contexts/RoomContext'; import type { ToolboxAction } from '../views/room/lib/Toolbox'; +import { useApplyButtonFilters, useApplyButtonAuthFilter } from './useApplyButtonFilters'; import { useUiKitActionManager } from './useUiKitActionManager'; const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`; @@ -49,13 +49,14 @@ export const useAppActionButtons = (context?: `${UIActionButtonContext}`) => { export const useMessageboxAppsActionButtons = () => { const result = useAppActionButtons('messageBoxAction'); const actionManager = useUiKitActionManager(); - const room = useRoom(); + + const applyButtonFilters = useApplyButtonFilters(); const data = useMemo( () => result.data ?.filter((action) => { - return applyButtonFilters(action, room); + return applyButtonFilters(action); }) .map((action) => { const item: MessageBoxAction = { @@ -74,7 +75,7 @@ export const useMessageboxAppsActionButtons = () => { return item; }), - [actionManager, result.data, room], + [actionManager, applyButtonFilters, result.data], ); return { ...result, @@ -86,6 +87,8 @@ export const useUserDropdownAppsActionButtons = () => { const result = useAppActionButtons('userDropdownAction'); const actionManager = useUiKitActionManager(); + const applyButtonFilters = useApplyButtonAuthFilter(); + const data = useMemo( () => result.data @@ -106,7 +109,7 @@ export const useUserDropdownAppsActionButtons = () => { }, }; }), - [actionManager, result.data], + [actionManager, applyButtonFilters, result.data], ); return { ...result, @@ -117,6 +120,7 @@ export const useUserDropdownAppsActionButtons = () => { export const useRoomActionAppsActionButtons = (context?: MessageActionContext) => { const result = useAppActionButtons('roomAction'); const actionManager = useUiKitActionManager(); + const applyButtonFilters = useApplyButtonFilters(); const room = useRoom(); const data = useMemo( () => @@ -125,7 +129,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) = if (context && ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'].includes(context)) { return false; } - return applyButtonFilters(action, room); + return applyButtonFilters(action); }) .map((action) => { const item: [string, ToolboxAction] = [ @@ -149,7 +153,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) = ]; return item; }), - [actionManager, context, result.data, room], + [actionManager, applyButtonFilters, context, result.data, room._id], ); return { ...result, @@ -160,8 +164,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) = export const useMessageActionAppsActionButtons = (context?: MessageActionContext) => { const result = useAppActionButtons('messageAction'); const actionManager = useUiKitActionManager(); - const room = useRoom(); - + const applyButtonFilters = useApplyButtonFilters(); const data = useMemo( () => result.data @@ -172,7 +175,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext ) { return false; } - return applyButtonFilters(action, room); + return applyButtonFilters(action); }) .map((action) => { const item: MessageActionConfig = { @@ -192,7 +195,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext return item; }), - [actionManager, context, result.data, room], + [actionManager, applyButtonFilters, context, result.data], ); return { ...result, diff --git a/apps/meteor/client/hooks/useApplyButtonFilters.ts b/apps/meteor/client/hooks/useApplyButtonFilters.ts new file mode 100644 index 000000000000..742f33489deb --- /dev/null +++ b/apps/meteor/client/hooks/useApplyButtonFilters.ts @@ -0,0 +1,65 @@ +import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui'; +import { RoomTypeFilter } from '@rocket.chat/apps-engine/definition/ui'; +import type { IRoom } from '@rocket.chat/core-typings'; +import { + isDirectMessageRoom, + isMultipleDirectMessageRoom, + isOmnichannelRoom, + isPrivateDiscussion, + isPrivateTeamRoom, + isPublicDiscussion, + isPublicTeamRoom, +} from '@rocket.chat/core-typings'; +import { AuthorizationContext, useUserId } from '@rocket.chat/ui-contexts'; +import { useCallback, useContext } from 'react'; + +import { useRoom } from '../views/room/contexts/RoomContext'; + +const enumToFilter: { [k in RoomTypeFilter]: (room: IRoom) => boolean } = { + [RoomTypeFilter.PUBLIC_CHANNEL]: (room) => room.t === 'c', + [RoomTypeFilter.PRIVATE_CHANNEL]: (room) => room.t === 'p', + [RoomTypeFilter.PUBLIC_TEAM]: isPublicTeamRoom, + [RoomTypeFilter.PRIVATE_TEAM]: isPrivateTeamRoom, + [RoomTypeFilter.PUBLIC_DISCUSSION]: isPublicDiscussion, + [RoomTypeFilter.PRIVATE_DISCUSSION]: isPrivateDiscussion, + [RoomTypeFilter.DIRECT]: isDirectMessageRoom, + [RoomTypeFilter.DIRECT_MULTIPLE]: isMultipleDirectMessageRoom, + [RoomTypeFilter.LIVE_CHAT]: isOmnichannelRoom, +}; + +const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => { + const { roomTypes } = button.when || {}; + return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room)); +}; + +export const useApplyButtonFilters = (): ((button: IUIActionButton) => boolean) => { + const room = useRoom(); + if (!room) { + throw new Error('useApplyButtonFilters must be used inside a room context'); + } + const applyAuthFilter = useApplyButtonAuthFilter(); + return useCallback( + (button: IUIActionButton) => applyAuthFilter(button) && (!room || applyRoomFilter(button, room)), + [applyAuthFilter, room], + ); +}; + +export const useApplyButtonAuthFilter = (): ((button: IUIActionButton) => boolean) => { + const uid = useUserId(); + + const { queryAllPermissions, queryAtLeastOnePermission, queryRole } = useContext(AuthorizationContext); + + return useCallback( + (button: IUIActionButton, room?: IRoom) => { + const { hasAllPermissions, hasOnePermission, hasAllRoles, hasOneRole } = button.when || {}; + + const hasAllPermissionsResult = hasAllPermissions ? queryAllPermissions(hasAllPermissions)[1]() : true; + const hasOnePermissionResult = hasOnePermission ? queryAtLeastOnePermission(hasOnePermission)[1]() : true; + const hasAllRolesResult = hasAllRoles ? !!uid && hasAllRoles.every((role) => queryRole(role, room?._id)) : true; + const hasOneRoleResult = hasOneRole ? !!uid && hasOneRole.some((role) => queryRole(role, room?._id)[1]()) : true; + + return hasAllPermissionsResult && hasOnePermissionResult && hasAllRolesResult && hasOneRoleResult; + }, + [queryAllPermissions, queryAtLeastOnePermission, queryRole, uid], + ); +}; diff --git a/apps/meteor/client/providers/AuthorizationProvider.tsx b/apps/meteor/client/providers/AuthorizationProvider.tsx index 8fb0e69d12a5..64d936b5cd65 100644 --- a/apps/meteor/client/providers/AuthorizationProvider.tsx +++ b/apps/meteor/client/providers/AuthorizationProvider.tsx @@ -20,7 +20,10 @@ const contextValue = { queryPermission: createReactiveSubscriptionFactory((permission, scope, scopeRoles) => hasPermission(permission, scope, scopeRoles)), queryAtLeastOnePermission: createReactiveSubscriptionFactory((permissions, scope) => hasAtLeastOnePermission(permissions, scope)), queryAllPermissions: createReactiveSubscriptionFactory((permissions, scope) => hasAllPermission(permissions, scope)), - queryRole: createReactiveSubscriptionFactory((role) => !!Meteor.userId() && hasRole(Meteor.userId() as string, role)), + queryRole: createReactiveSubscriptionFactory( + (role, scope?, ignoreSubscriptions = false) => + !!Meteor.userId() && hasRole(Meteor.userId() as string, role, scope, ignoreSubscriptions), + ), roleStore: new RoleStore(), }; diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx new file mode 100644 index 000000000000..a520444389a2 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx @@ -0,0 +1,488 @@ +/* eslint-disable react/no-multi-comp */ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { ActionManagerContext } from '../../../../contexts/ActionManagerContext'; +import { useAppsItems } from './useAppsItems'; + +it('should return and empty array if the user does not have `manage-apps` and `access-marketplace` permission', () => { + const { result } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={(args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [] as any; + } + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + expect(result.all[0]).toEqual([]); +}); + +it('should return `marketplace` and `installed` items if the user has `access-marketplace` permission', () => { + const { result } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={(args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [] as any; + } + }} + > + <MockedAuthorizationContext permissions={['access-marketplace']}> + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedAuthorizationContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); +}); + +it('should return `marketplace` and `installed` items if the user has `manage-apps` permission', () => { + const { result } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedAuthorizationContext permissions={['manage-apps']}> + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedAuthorizationContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'requested-apps', + }), + ); +}); + +it('should return one action from the server with no conditions', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedAuthorizationContext permissions={['manage-apps']}> + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedAuthorizationContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + await waitForValueToChange(() => result.current[3]); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'APP_ID_ACTION_ID', + }), + ); +}); + +describe('User Dropdown actions with role conditions', () => { + it('should return the action if the user has admin role', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOneRole: ['admin'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedAuthorizationContext permissions={['manage-apps']} roles={['admin']}> + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedAuthorizationContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'APP_ID_ACTION_ID', + }), + ); + }); + + it('should return filter the action if the user doesn`t have admin role', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOneRole: ['admin'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedAuthorizationContext permissions={['manage-apps']}> + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedAuthorizationContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'requested-apps', + }), + ); + + expect(result.current[3]).toEqual(undefined); + }); +}); + +describe('User Dropdown actions with permission conditions', () => { + it('should return the action if the user has manage-apps permission', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOnePermission: ['manage-apps'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedAuthorizationContext permissions={['manage-apps']}> + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedAuthorizationContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'marketplace', + }), + ); + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'installed', + }), + ); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'APP_ID_ACTION_ID', + }), + ); + }); + + it('should return filter the action if the user doesn`t have `any` permission', async () => { + const { result, waitForValueToChange } = renderHook( + () => { + return useAppsItems(); + }, + { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') { + return { + data: { + totalSeen: 0, + totalUnseen: 1, + }, + } as any; + } + if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') { + return [ + { + appId: 'APP_ID', + actionId: 'ACTION_ID', + labelI18n: 'LABEL_I18N', + context: 'userDropdownAction', + when: { + hasOnePermission: ['any'], + }, + }, + ] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedAuthorizationContext permissions={['manage-apps']}> + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedUiKitActionManager>{children}</MockedUiKitActionManager> + </MockedUserContext> + </MockedSettingsContext> + </MockedAuthorizationContext> + </MockedServerContext> + </QueryClientProvider> + ), + }, + ); + + await waitForValueToChange(() => { + return queryClient.isFetching(); + }); + + expect(result.current[3]).toEqual(undefined); + }); +}); + +export const MockedUiKitActionManager = ({ children }: { children: React.ReactNode }) => { + return <ActionManagerContext.Provider value={{} as any}>{children}</ActionManagerContext.Provider>; +}; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, +}); +afterEach(() => { + queryClient.clear(); +}); diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.client.config.ts index db3a03ecf03f..bcd9f8f5432a 100644 --- a/apps/meteor/jest.client.config.ts +++ b/apps/meteor/jest.client.config.ts @@ -3,7 +3,11 @@ export default { testEnvironment: 'jsdom', modulePathIgnorePatterns: ['<rootDir>/dist/'], - testMatch: ['<rootDir>/client/hooks/**.spec.[jt]s?(x)', '<rootDir>/client/components/**.spec.[jt]s?(x)'], + testMatch: [ + '<rootDir>/client/hooks/**.spec.[jt]s?(x)', + '<rootDir>/client/components/**.spec.[jt]s?(x)', + '<rootDir>/client/sidebar/header/actions/hooks/**/**.spec.[jt]s?(x)', + ], transform: { '^.+\\.(t|j)sx?$': '@swc/jest', }, diff --git a/packages/mock-providers/src/MockedAuthorizationContext.tsx b/packages/mock-providers/src/MockedAuthorizationContext.tsx index 18f791f1dd98..4d2a5c05a473 100644 --- a/packages/mock-providers/src/MockedAuthorizationContext.tsx +++ b/packages/mock-providers/src/MockedAuthorizationContext.tsx @@ -1,14 +1,25 @@ import React from 'react'; import { AuthorizationContext } from '@rocket.chat/ui-contexts'; -export const MockedAuthorizationContext = ({ permissions = [], children }: { permissions: string[]; children: React.ReactNode }) => { +export const MockedAuthorizationContext = ({ + permissions = [], + roles = [], + children, +}: { + permissions: string[]; + roles?: string[]; + children: React.ReactNode; +}) => { return ( <AuthorizationContext.Provider value={{ queryPermission: (id: string) => [() => (): void => undefined, (): boolean => permissions.includes(id)], - queryAtLeastOnePermission: () => [() => (): void => undefined, (): boolean => false], - queryAllPermissions: () => [() => (): void => undefined, (): boolean => false], - queryRole: () => [() => (): void => undefined, (): boolean => false], + queryAtLeastOnePermission: (ids: string[]) => [ + () => (): void => undefined, + (): boolean => ids.some((id) => permissions.includes(id)), + ], + queryAllPermissions: (ids: string[]) => [() => (): void => undefined, (): boolean => ids.every((id) => permissions.includes(id))], + queryRole: (id: string) => [() => (): void => undefined, (): boolean => roles.includes(id)], roleStore: { roles: {}, emit: (): void => undefined, diff --git a/packages/mock-providers/src/MockedServerContext.tsx b/packages/mock-providers/src/MockedServerContext.tsx index f3c13004ed77..6b23664f8167 100644 --- a/packages/mock-providers/src/MockedServerContext.tsx +++ b/packages/mock-providers/src/MockedServerContext.tsx @@ -33,6 +33,7 @@ export const MockedServerContext = ({ }) => { return handleRequest(args); }, + getStream: () => () => undefined, } as any } > diff --git a/packages/ui-contexts/src/AuthorizationContext.ts b/packages/ui-contexts/src/AuthorizationContext.ts index a321c408672f..d077568e772e 100644 --- a/packages/ui-contexts/src/AuthorizationContext.ts +++ b/packages/ui-contexts/src/AuthorizationContext.ts @@ -1,4 +1,4 @@ -import type { IRole } from '@rocket.chat/core-typings'; +import type { IRole, IRoom } from '@rocket.chat/core-typings'; import type { IEmitter } from '@rocket.chat/emitter'; import { createContext } from 'react'; import type { ObjectId } from 'mongodb'; @@ -27,7 +27,11 @@ export type AuthorizationContextValue = { scope?: string | ObjectId, scopedRoles?: IRole['_id'][], ): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean]; - queryRole(role: string | ObjectId): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean]; + queryRole( + role: string | ObjectId, + scope?: IRoom['_id'], + ignoreSubscriptions?: boolean, + ): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean]; roleStore: RoleStore; }; From 355e50d86cc6915a79604c2e4168f737027b64d3 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:10:47 +0530 Subject: [PATCH 115/149] refactor: replace useForm in favor of RHF on Prune Messages (#29704) Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com> --- .../PruneMessages/PruneMessages.stories.tsx | 18 ++- .../PruneMessages/PruneMessages.tsx | 53 ++----- .../PruneMessagesDateTimeRow.tsx | 35 ++-- .../PruneMessages/PruneMessagesWithData.tsx | 150 ++++++++++-------- apps/meteor/package.json | 2 +- 5 files changed, 130 insertions(+), 128 deletions(-) diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx index a894bca693ab..e0b653f85026 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.stories.tsx @@ -1,5 +1,6 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; import { Contextualbar } from '../../../../components/Contextualbar'; import PruneMessages from './PruneMessages'; @@ -11,7 +12,21 @@ export default { layout: 'fullscreen', actions: { argTypesRegex: '^on.*' }, }, - decorators: [(fn) => <Contextualbar height='100vh'>{fn()}</Contextualbar>], + decorators: [ + (fn) => { + const methods = useForm({ + defaultValues: { + pinned: true, + }, + }); + + return ( + <FormProvider {...methods}> + <Contextualbar height='100vh'>{fn()}</Contextualbar> + </FormProvider> + ); + }, + ], } as ComponentMeta<typeof PruneMessages>; const Template: ComponentStory<typeof PruneMessages> = (args) => <PruneMessages {...args} />; @@ -20,6 +35,5 @@ export const Default = Template.bind({}); export const WithCallout = Template.bind({}); WithCallout.args = { - values: { pinned: true }, callOutText: 'This is a callout', }; diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx index 3dd4f7862609..9f64bbc6504b 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessages.tsx @@ -3,6 +3,7 @@ import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import { useFormContext, Controller } from 'react-hook-form'; import { ContextualbarHeader, @@ -14,36 +15,18 @@ import { } from '../../../../components/Contextualbar'; import UserAutoCompleteMultiple from '../../../../components/UserAutoCompleteMultiple'; import PruneMessagesDateTimeRow from './PruneMessagesDateTimeRow'; -import type { initialValues } from './PruneMessagesWithData'; type PruneMessagesProps = { callOutText?: string; validateText?: string; users: string[]; - values: Record<string, unknown>; - handlers: Record<string, (eventOrValue: unknown) => void>; onClickClose: () => void; onClickPrune: () => void; }; -const PruneMessages = ({ callOutText, validateText, values, handlers, onClickClose, onClickPrune }: PruneMessagesProps): ReactElement => { +const PruneMessages = ({ callOutText, validateText, onClickClose, onClickPrune }: PruneMessagesProps): ReactElement => { const t = useTranslation(); - - const { newerDate, newerTime, olderDate, olderTime, users, inclusive, pinned, discussion, threads, attached } = - values as typeof initialValues; - - const { - handleNewerDate, - handleNewerTime, - handleOlderDate, - handleOlderTime, - handleInclusive, - handlePinned, - handleDiscussion, - handleThreads, - handleAttached, - handleUsers, - } = handlers; + const { control, register } = useFormContext(); const inclusiveCheckboxId = useUniqueId(); const pinnedCheckboxId = useUniqueId(); @@ -59,47 +42,45 @@ const PruneMessages = ({ callOutText, validateText, values, handlers, onClickClo {onClickClose && <ContextualbarClose onClick={onClickClose} />} </ContextualbarHeader> <ContextualbarScrollableContent> - <PruneMessagesDateTimeRow - label={t('Newer_than')} - dateTime={{ date: newerDate, time: newerTime }} - handleDateTime={{ date: handleNewerDate, time: handleNewerTime }} - /> - <PruneMessagesDateTimeRow - label={t('Older_than')} - dateTime={{ date: olderDate, time: olderTime }} - handleDateTime={{ date: handleOlderDate, time: handleOlderTime }} - /> + <PruneMessagesDateTimeRow label={t('Newer_than')} field='newer' /> + <PruneMessagesDateTimeRow label={t('Older_than')} field='older' /> <Field> <Field.Label flexGrow={0}>{t('Only_from_users')}</Field.Label> - <UserAutoCompleteMultiple value={users} onChange={handleUsers} placeholder={t('Please_enter_usernames')} /> + <Controller + control={control} + name='users' + render={({ field: { onChange, value } }) => ( + <UserAutoCompleteMultiple value={value} onChange={onChange} placeholder={t('Please_enter_usernames')} /> + )} + /> </Field> <Field> <Field.Row> - <CheckBox id={inclusiveCheckboxId} checked={inclusive} onChange={handleInclusive} /> + <CheckBox id={inclusiveCheckboxId} {...register('inclusive')} /> <Field.Label htmlFor={inclusiveCheckboxId}>{t('Inclusive')}</Field.Label> </Field.Row> </Field> <Field> <Field.Row> - <CheckBox id={pinnedCheckboxId} checked={pinned} onChange={handlePinned} /> + <CheckBox id={pinnedCheckboxId} {...register('pinned')} /> <Field.Label htmlFor={pinnedCheckboxId}>{t('RetentionPolicy_DoNotPrunePinned')}</Field.Label> </Field.Row> </Field> <Field> <Field.Row> - <CheckBox id={discussionCheckboxId} checked={discussion} onChange={handleDiscussion} /> + <CheckBox id={discussionCheckboxId} {...register('discussion')} /> <Field.Label htmlFor={discussionCheckboxId}>{t('RetentionPolicy_DoNotPruneDiscussion')}</Field.Label> </Field.Row> </Field> <Field> <Field.Row> - <CheckBox id={threadsCheckboxId} checked={threads} onChange={handleThreads} /> + <CheckBox id={threadsCheckboxId} {...register('threads')} /> <Field.Label htmlFor={threadsCheckboxId}>{t('RetentionPolicy_DoNotPruneThreads')}</Field.Label> </Field.Row> </Field> <Field> <Field.Row> - <CheckBox id={attachedCheckboxId} checked={attached} onChange={handleAttached} /> + <CheckBox id={attachedCheckboxId} {...register('attached')} /> <Field.Label htmlFor={attachedCheckboxId}>{t('Files_only')}</Field.Label> </Field.Row> </Field> diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx index bfb48f2859c8..9ca08a593d61 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesDateTimeRow.tsx @@ -1,29 +1,26 @@ import { Field, InputBox, Box, Margins } from '@rocket.chat/fuselage'; import type { ReactElement } from 'react'; import React from 'react'; +import { useFormContext } from 'react-hook-form'; type PruneMessagesDateTimeRowProps = { label: string; - dateTime: { - date: string; - time: string; - }; - handleDateTime: { - date: (eventOrValue: unknown) => void; - time: (eventOrValue: unknown) => void; - }; + field: 'newer' | 'older'; }; -const PruneMessagesDateTimeRow = ({ label, dateTime, handleDateTime }: PruneMessagesDateTimeRowProps): ReactElement => ( - <Field> - <Field.Label flexGrow={0}>{label}</Field.Label> - <Box display='flex' mi='neg-x4'> - <Margins inline='x4'> - <InputBox type='date' value={dateTime?.date} onChange={handleDateTime?.date} flexGrow={1} h='x20' /> - <InputBox type='time' value={dateTime?.time} onChange={handleDateTime?.time} flexGrow={1} h='x20' /> - </Margins> - </Box> - </Field> -); +const PruneMessagesDateTimeRow = ({ label, field }: PruneMessagesDateTimeRowProps): ReactElement => { + const { register } = useFormContext(); + return ( + <Field> + <Field.Label flexGrow={0}>{label}</Field.Label> + <Box display='flex' mi='neg-x4'> + <Margins inline='x4'> + <InputBox type='date' flexGrow={1} h='x20' {...register(`${field}.date`)} /> + <InputBox type='time' flexGrow={1} h='x20' {...register(`${field}.time`)} /> + </Margins> + </Box> + </Field> + ); +}; export default PruneMessagesDateTimeRow; diff --git a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx index abbde3da6250..b294df0af5d8 100644 --- a/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx +++ b/apps/meteor/client/views/room/contextualBar/PruneMessages/PruneMessagesWithData.tsx @@ -4,10 +4,10 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useUserRoom, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import moment from 'moment'; import type { ReactElement } from 'react'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; +import { useForm, FormProvider } from 'react-hook-form'; import GenericModal from '../../../../components/GenericModal'; -import { useForm } from '../../../../hooks/useForm'; import type { ToolboxContextValue } from '../../contexts/ToolboxContext'; import PruneMessages from './PruneMessages'; @@ -18,10 +18,14 @@ const getTimeZoneOffset = (): string => { }; export const initialValues = { - newerDate: '', - newerTime: '', - olderDate: '', - olderTime: '', + newer: { + date: '', + time: '', + }, + older: { + date: '', + time: '', + }, users: [], inclusive: false, pinned: false, @@ -41,18 +45,31 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too const dispatchToastMessage = useToastMessageDispatch(); const pruneMessagesAction = useEndpoint('POST', '/v1/rooms.cleanHistory'); - const [fromDate, setFromDate] = useState(new Date('0001-01-01T00:00:00Z')); - const [toDate, setToDate] = useState(new Date('9999-12-31T23:59:59Z')); - const [callOutText, setCallOutText] = useState<string | undefined>(); - const [validateText, setValidateText] = useState<string | undefined>(); const [counter, setCounter] = useState(0); - const { values, handlers, reset } = useForm(initialValues); - const { newerDate, newerTime, olderDate, olderTime, users, inclusive, pinned, discussion, threads, attached } = - values as typeof initialValues; + const methods = useForm({ defaultValues: initialValues }); + + const { + newer: { date: newerDate, time: newerTime }, + older: { date: olderDate, time: olderTime }, + users, + inclusive, + pinned, + discussion, + threads, + attached, + } = methods.watch(); + + const fromDate = useMemo(() => { + return new Date(`${newerDate || '0001-01-01'}T${newerTime || '00:00'}:00${getTimeZoneOffset()}`); + }, [newerDate, newerTime]); + + const toDate = useMemo(() => { + return new Date(`${olderDate || '9999-12-31'}T${olderTime || '23:59'}:59${getTimeZoneOffset()}`); + }, [olderDate, olderTime]); const handlePrune = useMutableCallback((): void => { - const handlePruneAction = async (): Promise<void> => { + const handlePruneAction = async () => { const limit = DEFAULT_PRUNE_LIMIT; try { @@ -80,10 +97,10 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too } dispatchToastMessage({ type: 'success', message: t('__count__message_pruned', { count }) }); - closeModal(); - reset(); + methods.reset(); } catch (error: unknown) { dispatchToastMessage({ type: 'error', message: error }); + } finally { closeModal(); } }; @@ -101,17 +118,7 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too ); }); - useEffect(() => { - if (newerDate) { - setFromDate(new Date(`${newerDate}T${newerTime || '00:00'}:00${getTimeZoneOffset()}`)); - } - - if (olderDate) { - setToDate(new Date(`${olderDate}T${olderTime || '24:00'}:00${getTimeZoneOffset()}`)); - } - }, [newerDate, newerTime, olderDate, olderTime]); - - useEffect(() => { + const callOutText = useMemo(() => { const exceptPinned = pinned ? ` ${t('except_pinned', {})}` : ''; const ifFrom = users.length ? ` ${t('if_they_are_from', { @@ -122,73 +129,76 @@ const PruneMessagesWithData = ({ rid, tabBar }: { rid: IRoom['_id']; tabBar: Too const filesOrMessages = t(attached ? 'files' : 'messages', {}); if (newerDate && olderDate) { - setCallOutText( + return ( t('Prune_Warning_between', { postProcess: 'sprintf', sprintf: [filesOrMessages, name, moment(fromDate).format('L LT'), moment(toDate).format('L LT')], }) + - exceptPinned + - ifFrom, + exceptPinned + + ifFrom ); - } else if (newerDate) { - setCallOutText( + } + + if (newerDate) { + return ( t('Prune_Warning_after', { postProcess: 'sprintf', sprintf: [filesOrMessages, name, moment(fromDate).format('L LT')], }) + - exceptPinned + - ifFrom, + exceptPinned + + ifFrom ); - } else if (olderDate) { - setCallOutText( + } + + if (olderDate) { + return ( t('Prune_Warning_before', { postProcess: 'sprintf', sprintf: [filesOrMessages, name, moment(toDate).format('L LT')], }) + - exceptPinned + - ifFrom, - ); - } else { - setCallOutText( - t('Prune_Warning_all', { - postProcess: 'sprintf', - sprintf: [filesOrMessages, room && isDirectMessageRoom(room) && (room.name || room.usernames?.join(' x '))], - }) + - exceptPinned + - ifFrom, + exceptPinned + + ifFrom ); } + return ( + t('Prune_Warning_all', { + postProcess: 'sprintf', + sprintf: [filesOrMessages, room && ((isDirectMessageRoom(room) && room.usernames?.join(' x ')) || room.fname || room.name)], + }) + + exceptPinned + + ifFrom + ); + }, [attached, fromDate, newerDate, olderDate, pinned, room, t, toDate, users]); + + const validateText = useMemo(() => { if (fromDate > toDate) { - return setValidateText( - t('Newer_than_may_not_exceed_Older_than', { - postProcess: 'sprintf', - sprintf: [], - }), - ); + return t('Newer_than_may_not_exceed_Older_than', { + postProcess: 'sprintf', + sprintf: [], + }); } + if (isNaN(fromDate.getTime()) || isNaN(toDate.getTime())) { - return setValidateText( - t('error-invalid-date', { - postProcess: 'sprintf', - sprintf: [], - }), - ); + return t('error-invalid-date', { + postProcess: 'sprintf', + sprintf: [], + }); } - setValidateText(undefined); - }, [newerDate, olderDate, fromDate, toDate, attached, t, pinned, users, room]); + return undefined; + }, [fromDate, t, toDate]); return ( - <PruneMessages - callOutText={callOutText} - validateText={validateText} - users={users} - values={values} - handlers={handlers} - onClickClose={onClickClose} - onClickPrune={handlePrune} - /> + <FormProvider {...methods}> + <PruneMessages + callOutText={callOutText} + validateText={validateText} + users={users} + onClickClose={onClickClose} + onClickPrune={handlePrune} + /> + </FormProvider> ); }; diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 7235923ab100..ffef9505b2e6 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -49,7 +49,7 @@ "version": "node .scripts/version.js", "set-version": "node .scripts/set-version.js", "release": "meteor npm run set-version --silent", - "storybook": "cross-env NODE_OPTIONS=--max-old-space-size=8192 start-storybook -p 6006", + "storybook": "cross-env NODE_OPTIONS=--max-old-space-size=8192 start-storybook -p 6006 --no-version-updates", "docker:start": "docker-compose up" }, "license": "MIT", From 4643466994b9846c6887bcd7cae7438b74d5c139 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista <tasso.evangelista@rocket.chat> Date: Mon, 17 Jul 2023 18:44:48 -0300 Subject: [PATCH 116/149] regression: Router not capturing search string parameters (#29846) --- .../meteor/client/providers/RouterProvider.tsx | 2 +- .../externals/meteor/kadira-flow-router.d.ts | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/meteor/client/providers/RouterProvider.tsx b/apps/meteor/client/providers/RouterProvider.tsx index af0448ce34e8..0dd7ee31deed 100644 --- a/apps/meteor/client/providers/RouterProvider.tsx +++ b/apps/meteor/client/providers/RouterProvider.tsx @@ -126,7 +126,7 @@ const routesSubscribers = new Set<() => void>(); const updateFlowRouter = () => { if (FlowRouter._initialized) { FlowRouter._updateCallbacks(); - FlowRouter._page.dispatch({ path: FlowRouter._current.path, params: {} }); + FlowRouter._page.dispatch(new FlowRouter._page.Context(FlowRouter._current.path)); return; } diff --git a/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts b/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts index 8dfc0de5a01b..946d3d074e26 100644 --- a/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts +++ b/apps/meteor/definition/externals/meteor/kadira-flow-router.d.ts @@ -164,14 +164,20 @@ declare module 'meteor/kadira:flow-router' { _current: Current; } + namespace page { + function start(): void; + function stop(): void; + function show(path: string): void; + function dispatch(ctx: page.Context): void; + + class Context { + constructor(path: string, state?: object): Context; + } + } + const FlowRouter: Router & { Route: typeof Route; Router: typeof Router; - _page: { - start(): void; - stop(): void; - show(path: string): void; - dispatch(ctx: { path: string; params: any }): void; - }; + _page: typeof page; }; } From 53822be591971229e961db19749b9e1f571e9c02 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Mon, 17 Jul 2023 23:20:49 -0300 Subject: [PATCH 117/149] test: Cover sidebar audit/administration items hooks (#29821) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- .../client/hooks/useAppActionButtons.ts | 4 +- .../hooks/useAdministrationItems.spec.tsx | 203 ++++++++++++++++++ .../actions/hooks/useAdministrationItems.tsx | 15 +- .../actions/hooks/useAuditItems.spec.tsx | 171 +++++++++++++++ .../client/views/hooks/useUpgradeTabParams.ts | 2 +- .../{jest.client.config.ts => jest.config.ts} | 0 apps/meteor/package.json | 2 +- .../mock-providers/src/MockedModalContext.tsx | 20 ++ .../src/MockedServerContext.tsx | 16 +- packages/mock-providers/src/index.ts | 5 + .../src/hooks/useFeaturePreviewList.spec.tsx | 33 +++ 11 files changed, 456 insertions(+), 15 deletions(-) create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx rename apps/meteor/{jest.client.config.ts => jest.config.ts} (100%) create mode 100644 packages/mock-providers/src/MockedModalContext.tsx create mode 100644 packages/mock-providers/src/index.ts diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts index 6d5aac3e92a3..4eb28a393d1b 100644 --- a/apps/meteor/client/hooks/useAppActionButtons.ts +++ b/apps/meteor/client/hooks/useAppActionButtons.ts @@ -95,9 +95,9 @@ export const useUserDropdownAppsActionButtons = () => { ?.filter((action) => { return applyButtonFilters(action); }) - .map((action, key) => { + .map((action) => { return { - id: action.actionId + key, + id: `${action.appId}_${action.actionId}`, // icon: action.icon as GenericMenuItemProps['icon'], content: action.labelI18n, onClick: () => { diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx new file mode 100644 index 000000000000..a050e0f48060 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.spec.tsx @@ -0,0 +1,203 @@ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedModalContext } from '@rocket.chat/mock-providers/src/MockedModalContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { useAdministrationItems } from './useAdministrationItems'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, +}); + +beforeEach(() => { + queryClient.clear(); +}); + +it('should not show upgrade item if has license and not have trial', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: ['testModule'], + meta: { trial: false }, + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={[]}> + <MockedModalContext>{children}</MockedModalContext> + </MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.all.length > 1)); + expect(result.current).toEqual([]); +}); + +it('should return an upgrade item if not have license or if have a trial', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: [], + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={[]}> + <MockedModalContext>{children}</MockedModalContext> + </MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => { + return !queryClient.isFetching(); + }); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'showUpgradeItem', + }), + ); +}); + +it('should return omnichannel item if has `view-livechat-manager` permission ', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: [], + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={['view-livechat-manager']}> + <MockedModalContext>{children}</MockedModalContext> + </MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'omnichannel', + }), + ); +}); + +it('should show administration item if has at least one admin permission', async () => { + const { result, waitFor } = renderHook(() => useAdministrationItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={async (args) => { + if (args.method === 'GET' && args.pathPattern === '/v1/licenses.get') { + return { + licenses: [ + { + modules: [], + }, + ], + } as any; + } + + if (args.method === 'GET' && args.pathPattern === '/v1/cloud.registrationStatus') { + return { + registrationStatus: { + workspaceRegistered: false, + }, + }; + } + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={['access-permissions']}> + <MockedModalContext>{children}</MockedModalContext> + </MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'workspace', + }), + ); +}); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index 3d4d640fecf2..ea678413a16b 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -1,20 +1,19 @@ import { useTranslation, useRoute, - useMethod, useSetModal, useRole, useRouter, useAtLeastOnePermission, usePermission, } from '@rocket.chat/ui-contexts'; -import { useQuery } from '@tanstack/react-query'; import React from 'react'; import type { UpgradeTabVariant } from '../../../../../lib/upgradeTab'; import { getUpgradeTabLabel, isFullyFeature } from '../../../../../lib/upgradeTab'; import Emoji from '../../../../components/Emoji'; import type { GenericMenuItemProps } from '../../../../components/GenericMenu/GenericMenuItem'; +import { useRegistrationStatus } from '../../../../hooks/useRegistrationStatus'; import RegisterWorkspaceModal from '../../../../views/admin/cloud/modals/RegisterWorkspaceModal'; import { useUpgradeTabParams } from '../../../../views/hooks/useUpgradeTabParams'; @@ -53,8 +52,8 @@ const ADMIN_PERMISSIONS = [ */ export const useAdministrationItems = (): GenericMenuItemProps[] => { - const router = useRouter(); const t = useTranslation(); + const router = useRouter(); const shouldShowAdminMenu = useAtLeastOnePermission(ADMIN_PERMISSIONS); @@ -66,9 +65,13 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => { const isAdmin = useRole('admin'); const setModal = useSetModal(); - const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); - const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); - const { workspaceRegistered } = result.data || {}; + // TODO: DEPRECATE IT + // const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); + // const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); + // const { workspaceRegistered } = result.data || {}; + + const { data: registrationStatusData } = useRegistrationStatus(); + const workspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; const handleRegisterWorkspaceClick = (): void => { const handleModalClose = (): void => setModal(null); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx new file mode 100644 index 000000000000..1cf43158db7c --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAuditItems.spec.tsx @@ -0,0 +1,171 @@ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { useAuditItems } from './useAuditItems'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, +}); + +it('should return an empty array if doesn`t have license', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleMethod={(methodName, ..._) => { + if (methodName === 'license:getModules') { + return [] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={['can-audit', 'can-audit-log']}>{children}</MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.all.length > 1)); + expect(result.current).toEqual([]); +}); + +it('should return an empty array if have license and not have permissions', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleMethod={(methodName, ..._) => { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={[]}>{children}</MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.all.length > 1)); + expect(result.current).toEqual([]); +}); + +it('should return auditItems if have license and permissions', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleMethod={(methodName, ..._) => { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={['can-audit', 'can-audit-log']}>{children}</MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'messages', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'auditLog', + }), + ); +}); + +it('should return auditMessages item if have license and can-audit permission', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleMethod={(methodName, ..._) => { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={['can-audit']}>{children}</MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'messages', + }), + ); +}); + +it('should return audiLogs item if have license and can-audit-log permission', async () => { + const { result, waitFor } = renderHook(() => useAuditItems(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleMethod={(methodName, ..._) => { + if (methodName === 'license:getModules') { + return ['auditing'] as any; + } + + throw new Error('Method not mocked'); + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={['can-audit-log']}>{children}</MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + await waitFor(() => Boolean(result.current.length)); + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'auditLog', + }), + ); +}); diff --git a/apps/meteor/client/views/hooks/useUpgradeTabParams.ts b/apps/meteor/client/views/hooks/useUpgradeTabParams.ts index 13cb2850d836..e051b69db8fa 100644 --- a/apps/meteor/client/views/hooks/useUpgradeTabParams.ts +++ b/apps/meteor/client/views/hooks/useUpgradeTabParams.ts @@ -13,7 +13,7 @@ export const useUpgradeTabParams = (): { tabType: UpgradeTabVariant | false; tri const { data: registrationStatusData, isSuccess: isSuccessRegistrationStatus } = useRegistrationStatus(); const registered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; - const hasValidLicense = licensesData?.licenses.some((licence) => licence.modules.length > 0) ?? false; + const hasValidLicense = licensesData?.licenses.some((license) => license.modules.length > 0) ?? false; const hadExpiredTrials = cloudWorkspaceHadTrial ?? false; const trialLicense = licensesData?.licenses?.find(({ meta }) => meta?.trial); diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.config.ts similarity index 100% rename from apps/meteor/jest.client.config.ts rename to apps/meteor/jest.config.ts diff --git a/apps/meteor/package.json b/apps/meteor/package.json index ffef9505b2e6..f3e19d15a50c 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -39,7 +39,7 @@ "testapi": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.api.js", "testunit": "npm run .testunit:definition && npm run .testunit:client && npm run .testunit:server", ".testunit:server": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.js", - ".testunit:client": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.client.js --exit", + ".testunit:client": "jest && TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.client.js --exit", ".testunit:definition": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --config ./.mocharc.definition.js", "testunit-watch": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha --watch --config ./.mocharc.js", "test": "npm run testapi && npm run testui", diff --git a/packages/mock-providers/src/MockedModalContext.tsx b/packages/mock-providers/src/MockedModalContext.tsx new file mode 100644 index 000000000000..4654b55ba135 --- /dev/null +++ b/packages/mock-providers/src/MockedModalContext.tsx @@ -0,0 +1,20 @@ +import type { ReactNode } from 'react'; +import React from 'react'; +import { ModalContext } from '@rocket.chat/ui-contexts'; + +export const MockedModalContext = ({ children }: { children: React.ReactNode }) => { + const [currentModal, setCurrentModal] = React.useState<ReactNode>(null); + + return ( + <ModalContext.Provider + value={{ + modal: { + setModal: setCurrentModal, + }, + currentModal, + }} + > + {children} + </ModalContext.Provider> + ); +}; diff --git a/packages/mock-providers/src/MockedServerContext.tsx b/packages/mock-providers/src/MockedServerContext.tsx index 6b23664f8167..32b9fb6068f5 100644 --- a/packages/mock-providers/src/MockedServerContext.tsx +++ b/packages/mock-providers/src/MockedServerContext.tsx @@ -1,20 +1,25 @@ import React from 'react'; import type { Serialized } from '@rocket.chat/core-typings'; import type { Method, OperationParams, OperationResult, PathPattern, UrlParams } from '@rocket.chat/rest-typings'; -import type { ServerMethodName, ServerMethodParameters } from '@rocket.chat/ui-contexts'; +import type { ServerMethodName, ServerMethodParameters, ServerMethodReturn } from '@rocket.chat/ui-contexts'; import { ServerContext } from '@rocket.chat/ui-contexts'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; export const MockedServerContext = ({ handleRequest, + handleMethod, children, }: { - handleRequest: <TMethod extends Method, TPathPattern extends PathPattern>(args: { + handleRequest?: <TMethod extends Method, TPathPattern extends PathPattern>(args: { method: TMethod; pathPattern: TPathPattern; keys: UrlParams<TPathPattern>; params: OperationParams<TMethod, TPathPattern>; }) => Promise<Serialized<OperationResult<TMethod, TPathPattern>>>; + handleMethod?: <MethodName extends ServerMethodName>( + methodName: MethodName, + ...args: ServerMethodParameters<MethodName> + ) => Promise<ServerMethodReturn<MethodName>>; children: React.ReactNode; }): any => { const [queryClient] = React.useState(() => new QueryClient()); @@ -23,15 +28,16 @@ export const MockedServerContext = ({ value={ { absoluteUrl: (path: string) => `http://localhost:3000/${path}`, - callMethod: <MethodName extends ServerMethodName>(_methodName: MethodName, ..._args: ServerMethodParameters<MethodName>) => - Promise.reject('mock not implemented'), + callMethod: <MethodName extends ServerMethodName>(methodName: MethodName, ...args: ServerMethodParameters<MethodName>) => { + return handleMethod?.(methodName, ...args); + }, callEndpoint: async <TMethod extends Method, TPathPattern extends PathPattern>(args: { method: TMethod; pathPattern: TPathPattern; keys: UrlParams<TPathPattern>; params: OperationParams<TMethod, TPathPattern>; }) => { - return handleRequest(args); + return handleRequest?.(args); }, getStream: () => () => undefined, } as any diff --git a/packages/mock-providers/src/index.ts b/packages/mock-providers/src/index.ts new file mode 100644 index 000000000000..602316cbdd72 --- /dev/null +++ b/packages/mock-providers/src/index.ts @@ -0,0 +1,5 @@ +export * from './MockedAuthorizationContext'; +export * from './MockedModalContext'; +export * from './MockedServerContext'; +export * from './MockedSettingsContext'; +export * from './MockedUserContext'; diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx index 5e1aacd7197b..2de617554115 100644 --- a/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.spec.tsx @@ -71,3 +71,36 @@ it('should return 0 unseen features', () => { }), ); }); + +it('should ignore removed feature previews', () => { + const { result } = renderHook(() => useFeaturePreviewList(), { + wrapper: ({ children }) => ( + <MockedSettingsContext + settings={{ + Accounts_AllowFeaturePreview: true, + }} + > + <MockedUserContext + userPreferences={{ + featuresPreview: [ + { + name: 'oldFeature', + value: false, + }, + ], + }} + > + {children} + </MockedUserContext> + </MockedSettingsContext> + ), + }); + + expect(result.current).toEqual( + expect.objectContaining({ + featurePreviewEnabled: true, + unseenFeatures: defaultFeaturesPreview.length, + features: defaultFeaturesPreview, + }), + ); +}); From 3fb21241667a4b5f244c26c813c2cc5394154b3d Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Date: Tue, 18 Jul 2023 19:57:01 +0530 Subject: [PATCH 118/149] fix: misleading of 'total' in team members & members fixed (#29090) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> Co-authored-by: Hugo Costa <20212776+hugocostadev@users.noreply.github.com> --- .changeset/lazy-ghosts-design.md | 5 +++++ .../views/room/contextualBar/RoomMembers/RoomMembers.tsx | 8 ++------ apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 .changeset/lazy-ghosts-design.md diff --git a/.changeset/lazy-ghosts-design.md b/.changeset/lazy-ghosts-design.md new file mode 100644 index 000000000000..080e9986cebb --- /dev/null +++ b/.changeset/lazy-ghosts-design.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed misleading of 'total' in team members list inside Channel diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx index 780f17084d59..95f2c43d1a95 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx @@ -120,15 +120,11 @@ const RoomMembers = ({ {!loading && members.length <= 0 && <ContextualbarEmptyContent title={t('No_members_found')} />} - {!loading && members && members.length > 0 && ( + {!loading && members.length > 0 && ( <> <Box pi='x18' pb='x12'> <Box is='span' color='hint' fontScale='p2'> - {t('Showing')}: {members.length} - </Box> - - <Box is='span' color='hint' fontScale='p2' mis='x8'> - {t('Total')}: {total} + {t('Showing_current_of_total', { current: members.length, total })} </Box> </Box> diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 5b23d85fc71d..154bf6af2d6e 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -4640,6 +4640,7 @@ "Show_video": "Show video", "Showing": "Showing", "Showing_archived_results": "<p>Showing <b>%s</b> archived results</p>", + "Showing_current_of_total":"Showing {{current}} of {{total}}", "Showing_online_users": "Showing: <b>{{total_showing}}</b>, Online: {{online}}, Total: {{total}} users", "Showing_results": "<p>Showing <b>%s</b> results</p>", "Showing_results_of": "Showing results %s - %s of %s", From 817d29b4765ff63ca7ce9e0356a77ec9278a1a24 Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto <tiago.evangelista@rocket.chat> Date: Tue, 18 Jul 2023 11:27:10 -0300 Subject: [PATCH 119/149] regression: Calling inexistent functions AppMenu on action change (#29834) Co-authored-by: Tasso Evangelista <tasso.evangelista@rocket.chat> Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- apps/meteor/client/views/marketplace/AppMenu.js | 4 +++- apps/meteor/ee/client/apps/communication/websockets.js | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/meteor/client/views/marketplace/AppMenu.js b/apps/meteor/client/views/marketplace/AppMenu.js index b243dde200bd..6f90781e6101 100644 --- a/apps/meteor/client/views/marketplace/AppMenu.js +++ b/apps/meteor/client/views/marketplace/AppMenu.js @@ -14,8 +14,10 @@ import semver from 'semver'; import WarningModal from '../../components/WarningModal'; import IframeModal from './IframeModal'; import UninstallGrandfatheredAppModal from './components/UninstallGrandfatheredAppModal/UninstallGrandfatheredAppModal'; -import { appEnabledStatuses, handleAPIError, appButtonProps, warnEnableDisableApp } from './helpers'; +import { appEnabledStatuses, appButtonProps } from './helpers'; +import { handleAPIError } from './helpers/handleAPIError'; import { marketplaceActions } from './helpers/marketplaceActions'; +import { warnEnableDisableApp } from './helpers/warnEnableDisableApp'; import { useAppInstallationHandler } from './hooks/useAppInstallationHandler'; import { useAppsCountQuery } from './hooks/useAppsCountQuery'; import { useOpenAppPermissionsReviewModal } from './hooks/useOpenAppPermissionsReviewModal'; diff --git a/apps/meteor/ee/client/apps/communication/websockets.js b/apps/meteor/ee/client/apps/communication/websockets.js index e8060539a665..932fd062168f 100644 --- a/apps/meteor/ee/client/apps/communication/websockets.js +++ b/apps/meteor/ee/client/apps/communication/websockets.js @@ -35,7 +35,6 @@ export class AppWebsocketReceiver extends Emitter { sdk.stream('apps', [AppEvents.COMMAND_UPDATED], this.onCommandAddedOrUpdated); sdk.stream('apps', [AppEvents.COMMAND_REMOVED], this.onCommandRemovedOrDisabled); sdk.stream('apps', [AppEvents.COMMAND_DISABLED], this.onCommandRemovedOrDisabled); - sdk.stream('apps', [AppEvents.ACTIONS_CHANGED], this.onActionsChanged); } registerListener(event, listener) { @@ -70,6 +69,4 @@ export class AppWebsocketReceiver extends Emitter { onCommandRemovedOrDisabled = (command) => { delete slashCommands.commands[command]; }; - - // onActionsChanged = () => loadButtons(); } From 682d0bc05a5c56a7f2364d25d761b35947f004cc Mon Sep 17 00:00:00 2001 From: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:07:00 +0530 Subject: [PATCH 120/149] fix: Time format of Retention Policy (#29651) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .changeset/friendly-glasses-mate.md | 5 +++++ .../client/components/InfoPanel/RetentionPolicyCallout.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/friendly-glasses-mate.md diff --git a/.changeset/friendly-glasses-mate.md b/.changeset/friendly-glasses-mate.md new file mode 100644 index 000000000000..6a7a7b4f8546 --- /dev/null +++ b/.changeset/friendly-glasses-mate.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +fix: Time format of Retention Policy diff --git a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx index 9e261c88af77..27202afa496c 100644 --- a/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx +++ b/apps/meteor/client/components/InfoPanel/RetentionPolicyCallout.tsx @@ -13,7 +13,7 @@ type RetentionPolicyCalloutProps = { const RetentionPolicyCallout: FC<RetentionPolicyCalloutProps> = ({ filesOnlyDefault, excludePinnedDefault, maxAgeDefault }) => { const t = useTranslation(); - const time = useFormattedRelativeTime(maxAgeDefault * 1000 * 60 * 60 * 24); + const time = useFormattedRelativeTime(maxAgeDefault); return ( <Callout type='warning'> From 0cb3c71d86b06b3880751c0c7d3325ed8e778e49 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Tue, 18 Jul 2023 13:35:52 -0300 Subject: [PATCH 121/149] chore: Normalize `Contextualbar` footer buttons (#29789) --- .../admin/customEmoji/AddCustomEmoji.tsx | 84 ++-- .../admin/customEmoji/EditCustomEmoji.tsx | 93 ++-- .../admin/customSounds/AddCustomSound.tsx | 72 ++-- .../views/admin/customSounds/EditSound.tsx | 82 ++-- .../customUserStatus/CustomUserStatusForm.tsx | 80 ++-- .../client/views/admin/rooms/EditRoom.tsx | 274 ++++++------ .../client/views/admin/users/AddUser.js | 25 +- .../client/views/admin/users/EditUser.js | 27 +- .../client/views/admin/users/InviteUsers.tsx | 45 +- .../client/views/admin/users/UserForm.js | 400 +++++++++--------- .../Info/EditRoomInfo/EditChannel.js | 40 +- 11 files changed, 611 insertions(+), 611 deletions(-) diff --git a/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx index 8accf5c5e56c..6d12c25f5239 100644 --- a/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx @@ -3,7 +3,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, ChangeEvent } from 'react'; import React, { useCallback, useState } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import { useEndpointUpload } from '../../../hooks/useEndpointUpload'; import { useFileInput } from '../../../hooks/useFileInput'; @@ -75,48 +75,48 @@ const AddCustomEmoji = ({ close, onChange, ...props }: AddCustomEmojiProps): Rea }; return ( - <ContextualbarScrollableContent {...props}> - <Field> - <Field.Label>{t('Name')}</Field.Label> - <Field.Row> - <TextInput value={name} onChange={handleChangeName} placeholder={t('Name')} /> - </Field.Row> - {errors.name && <Field.Error>{t('error-the-field-is-required', { field: t('Name') })}</Field.Error>} - </Field> - <Field> - <Field.Label>{t('Aliases')}</Field.Label> - <Field.Row> - <TextInput value={aliases} onChange={handleChangeAliases} placeholder={t('Aliases')} /> - </Field.Row> - {errors.aliases && <Field.Error>{t('Custom_Emoji_Error_Same_Name_And_Alias')}</Field.Error>} - </Field> - <Field> - <Field.Label alignSelf='stretch' display='flex' justifyContent='space-between' alignItems='center'> - {t('Custom_Emoji')} - <Button square onClick={clickUpload}> - <Icon name='upload' size='x20' /> - </Button> - </Field.Label> - {errors.emoji && <Field.Error>{t('error-the-field-is-required', { field: t('Custom_Emoji') })}</Field.Error>} - {newEmojiPreview && ( - <Box display='flex' flexDirection='row' mi='neg-x4' justifyContent='center'> - <Margins inline='x4'> - <Box is='img' style={{ objectFit: 'contain' }} w='x120' h='x120' src={newEmojiPreview} /> - </Margins> - </Box> - )} - </Field> - <Field> - <Field.Row> - <ButtonGroup stretch w='full'> - <Button onClick={close}>{t('Cancel')}</Button> - <Button primary onClick={handleSave}> - {t('Save')} + <> + <ContextualbarScrollableContent {...props}> + <Field> + <Field.Label>{t('Name')}</Field.Label> + <Field.Row> + <TextInput value={name} onChange={handleChangeName} placeholder={t('Name')} /> + </Field.Row> + {errors.name && <Field.Error>{t('error-the-field-is-required', { field: t('Name') })}</Field.Error>} + </Field> + <Field> + <Field.Label>{t('Aliases')}</Field.Label> + <Field.Row> + <TextInput value={aliases} onChange={handleChangeAliases} placeholder={t('Aliases')} /> + </Field.Row> + {errors.aliases && <Field.Error>{t('Custom_Emoji_Error_Same_Name_And_Alias')}</Field.Error>} + </Field> + <Field> + <Field.Label alignSelf='stretch' display='flex' justifyContent='space-between' alignItems='center'> + {t('Custom_Emoji')} + <Button square onClick={clickUpload}> + <Icon name='upload' size='x20' /> </Button> - </ButtonGroup> - </Field.Row> - </Field> - </ContextualbarScrollableContent> + </Field.Label> + {errors.emoji && <Field.Error>{t('error-the-field-is-required', { field: t('Custom_Emoji') })}</Field.Error>} + {newEmojiPreview && ( + <Box display='flex' flexDirection='row' mi='neg-x4' justifyContent='center'> + <Margins inline='x4'> + <Box is='img' style={{ objectFit: 'contain' }} w='x120' h='x120' src={newEmojiPreview} /> + </Margins> + </Box> + )} + </Field> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button onClick={close}>{t('Cancel')}</Button> + <Button primary onClick={handleSave}> + {t('Save')} + </Button> + </ButtonGroup> + </ContextualbarFooter> + </> ); }; diff --git a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx index 19081fe6e901..188802bb7c87 100644 --- a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx @@ -3,7 +3,7 @@ import { useSetModal, useToastMessageDispatch, useAbsoluteUrl, useTranslation } import type { FC, ChangeEvent } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; import { useEndpointUpload } from '../../../hooks/useEndpointUpload'; @@ -130,50 +130,53 @@ const EditCustomEmoji: FC<EditCustomEmojiProps> = ({ close, onChange, data, ...p }; return ( - <ContextualbarScrollableContent {...props}> - <FieldGroup> - <Field> - <Field.Label>{t('Name')}</Field.Label> - <Field.Row> - <TextInput value={name} onChange={handleChangeName} placeholder={t('Name')} /> - </Field.Row> - {errors.name && <Field.Error>{t('error-the-field-is-required', { field: t('Name') })}</Field.Error>} - </Field> - <Field> - <Field.Label>{t('Aliases')}</Field.Label> - <Field.Row> - <TextInput value={aliases} onChange={handleChangeAliases} placeholder={t('Aliases')} /> - </Field.Row> - {errors.aliases && <Field.Error>{t('Custom_Emoji_Error_Same_Name_And_Alias')}</Field.Error>} - </Field> - <Field> - <Field.Label alignSelf='stretch' display='flex' justifyContent='space-between' alignItems='center'> - {t('Custom_Emoji')} - <IconButton icon='upload' secondary onClick={clickUpload} /> - </Field.Label> - {newEmojiPreview && ( - <Box display='flex' flexDirection='row' mbs='none' justifyContent='center'> - <Margins inline='x4'> - <Box is='img' style={{ objectFit: 'contain' }} w='x120' h='x120' src={newEmojiPreview} /> - </Margins> - </Box> - )} - </Field> - </FieldGroup> - <ButtonGroup stretch w='full'> - <Button onClick={close}>{t('Cancel')}</Button> - <Button primary onClick={handleSave} disabled={!hasUnsavedChanges}> - {t('Save')} - </Button> - </ButtonGroup> - - <ButtonGroup stretch w='full'> - <Button danger onClick={handleDeleteButtonClick}> - <Icon name='trash' mie='x4' /> - {t('Delete')} - </Button> - </ButtonGroup> - </ContextualbarScrollableContent> + <> + <ContextualbarScrollableContent {...props}> + <FieldGroup> + <Field> + <Field.Label>{t('Name')}</Field.Label> + <Field.Row> + <TextInput value={name} onChange={handleChangeName} placeholder={t('Name')} /> + </Field.Row> + {errors.name && <Field.Error>{t('error-the-field-is-required', { field: t('Name') })}</Field.Error>} + </Field> + <Field> + <Field.Label>{t('Aliases')}</Field.Label> + <Field.Row> + <TextInput value={aliases} onChange={handleChangeAliases} placeholder={t('Aliases')} /> + </Field.Row> + {errors.aliases && <Field.Error>{t('Custom_Emoji_Error_Same_Name_And_Alias')}</Field.Error>} + </Field> + <Field> + <Field.Label alignSelf='stretch' display='flex' justifyContent='space-between' alignItems='center'> + {t('Custom_Emoji')} + <IconButton icon='upload' secondary onClick={clickUpload} /> + </Field.Label> + {newEmojiPreview && ( + <Box display='flex' flexDirection='row' mbs='none' justifyContent='center'> + <Margins inline='x4'> + <Box is='img' style={{ objectFit: 'contain' }} w='x120' h='x120' src={newEmojiPreview} /> + </Margins> + </Box> + )} + </Field> + </FieldGroup> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button onClick={close}>{t('Cancel')}</Button> + <Button primary onClick={handleSave} disabled={!hasUnsavedChanges}> + {t('Save')} + </Button> + </ButtonGroup> + <ButtonGroup mbs='x8' stretch> + <Button danger onClick={handleDeleteButtonClick}> + <Icon name='trash' mie='x4' /> + {t('Delete')} + </Button> + </ButtonGroup> + </ContextualbarFooter> + </> ); }; diff --git a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx index 986422ea7c7c..35feae9113cc 100644 --- a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx @@ -3,7 +3,7 @@ import { useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat import type { ReactElement, FormEvent } from 'react'; import React, { useState, useCallback } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import { useFileInput } from '../../../hooks/useFileInput'; import { validate, createSoundData } from './lib'; @@ -81,41 +81,41 @@ const AddCustomSound = ({ goToNew, close, onChange, ...props }: AddCustomSoundPr }, [dispatchToastMessage, goToNew, name, onChange, saveAction, sound, t]); return ( - <ContextualbarScrollableContent {...props}> - <Field> - <Field.Label>{t('Name')}</Field.Label> - <Field.Row> - <TextInput - value={name} - onChange={(e: FormEvent<HTMLInputElement>): void => setName(e.currentTarget.value)} - placeholder={t('Name')} - /> - </Field.Row> - </Field> - <Field> - <Field.Label alignSelf='stretch'>{t('Sound_File_mp3')}</Field.Label> - <Box display='flex' flexDirection='row' mbs='none'> - <Margins inline='x4'> - <Button square onClick={clickUpload}> - <Icon name='upload' size='x20' /> - </Button> - {sound?.name || t('None')} - </Margins> - </Box> - </Field> - <Field> - <Field.Row> - <ButtonGroup stretch w='full'> - <Button mie='x4' onClick={close}> - {t('Cancel')} - </Button> - <Button primary onClick={handleSave} disabled={name === ''}> - {t('Save')} - </Button> - </ButtonGroup> - </Field.Row> - </Field> - </ContextualbarScrollableContent> + <> + <ContextualbarScrollableContent {...props}> + <Field> + <Field.Label>{t('Name')}</Field.Label> + <Field.Row> + <TextInput + value={name} + onChange={(e: FormEvent<HTMLInputElement>): void => setName(e.currentTarget.value)} + placeholder={t('Name')} + /> + </Field.Row> + </Field> + <Field> + <Field.Label alignSelf='stretch'>{t('Sound_File_mp3')}</Field.Label> + <Box display='flex' flexDirection='row' mbs='none'> + <Margins inline='x4'> + <Button square onClick={clickUpload}> + <Icon name='upload' size='x20' /> + </Button> + {sound?.name || t('None')} + </Margins> + </Box> + </Field> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button mie='x4' onClick={close}> + {t('Cancel')} + </Button> + <Button primary onClick={handleSave} disabled={name === ''}> + {t('Save')} + </Button> + </ButtonGroup> + </ContextualbarFooter> + </> ); }; diff --git a/apps/meteor/client/views/admin/customSounds/EditSound.tsx b/apps/meteor/client/views/admin/customSounds/EditSound.tsx index fdf45739cf7a..19e3958c74a0 100644 --- a/apps/meteor/client/views/admin/customSounds/EditSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/EditSound.tsx @@ -3,7 +3,7 @@ import { useSetModal, useToastMessageDispatch, useMethod, useTranslation } from import type { ReactElement, SyntheticEvent } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; import { useFileInput } from '../../../hooks/useFileInput'; import { validate, createSoundData } from './lib'; @@ -117,49 +117,43 @@ function EditSound({ close, onChange, data, ...props }: EditSoundProps): ReactEl const [clickUpload] = useFileInput(handleChangeFile, 'audio/mp3'); return ( - <ContextualbarScrollableContent {...props}> - <Field> - <Field.Label>{t('Name')}</Field.Label> - <Field.Row> - <TextInput - value={name} - onChange={(e: SyntheticEvent<HTMLInputElement>): void => setName(e.currentTarget.value)} - placeholder={t('Name')} - /> - </Field.Row> - </Field> - - <Field> - <Field.Label alignSelf='stretch'>{t('Sound_File_mp3')}</Field.Label> - <Box display='flex' flexDirection='row' mbs='none'> - <Margins inline='x4'> - <IconButton icon='upload' secondary onClick={clickUpload} /> - {sound?.name || 'none'} - </Margins> - </Box> - </Field> - - <Field> - <Field.Row> - <ButtonGroup stretch w='full'> - <Button onClick={close}>{t('Cancel')}</Button> - <Button primary onClick={handleSave} disabled={!hasUnsavedChanges}> - {t('Save')} - </Button> - </ButtonGroup> - </Field.Row> - </Field> - <Field> - <Field.Row> - <ButtonGroup stretch w='full'> - <Button danger onClick={handleDeleteButtonClick}> - <Icon name='trash' mie='x4' /> - {t('Delete')} - </Button> - </ButtonGroup> - </Field.Row> - </Field> - </ContextualbarScrollableContent> + <> + <ContextualbarScrollableContent {...props}> + <Field> + <Field.Label>{t('Name')}</Field.Label> + <Field.Row> + <TextInput + value={name} + onChange={(e: SyntheticEvent<HTMLInputElement>): void => setName(e.currentTarget.value)} + placeholder={t('Name')} + /> + </Field.Row> + </Field> + <Field> + <Field.Label alignSelf='stretch'>{t('Sound_File_mp3')}</Field.Label> + <Box display='flex' flexDirection='row' mbs='none'> + <Margins inline='x4'> + <IconButton icon='upload' secondary onClick={clickUpload} /> + {sound?.name || 'none'} + </Margins> + </Box> + </Field> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button onClick={close}>{t('Cancel')}</Button> + <Button primary onClick={handleSave} disabled={!hasUnsavedChanges}> + {t('Save')} + </Button> + </ButtonGroup> + <ButtonGroup mbs='x8' stretch> + <Button danger onClick={handleDeleteButtonClick}> + <Icon name='trash' mie='x4' /> + {t('Delete')} + </Button> + </ButtonGroup> + </ContextualbarFooter> + </> ); } diff --git a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx index 2c21db402199..f85172533ee5 100644 --- a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx +++ b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx @@ -1,12 +1,13 @@ import type { IUserStatus } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; import { FieldGroup, Button, ButtonGroup, TextInput, Field, Select, Icon } from '@rocket.chat/fuselage'; +import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useRoute, useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useCallback } from 'react'; import { useForm, Controller } from 'react-hook-form'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; type CustomUserStatusFormProps = { @@ -21,6 +22,7 @@ const CustomUserStatusForm = ({ onClose, onReload, status }: CustomUserStatusFor const setModal = useSetModal(); const route = useRoute('user-status'); const dispatchToastMessage = useToastMessageDispatch(); + const formId = useUniqueId(); const { register, @@ -86,51 +88,47 @@ const CustomUserStatusForm = ({ onClose, onReload, status }: CustomUserStatusFor ]; return ( - <ContextualbarScrollableContent> - <FieldGroup is='form' onSubmit={handleSubmit(handleSave)}> - <Field> - <Field.Label>{t('Name')}</Field.Label> - <Field.Row> - <TextInput {...register('name', { required: true })} placeholder={t('Name')} /> - </Field.Row> - {errors?.name && <Field.Error>{t('error-the-field-is-required', { field: t('Name') })}</Field.Error>} - </Field> - <Field> - <Field.Label>{t('Presence')}</Field.Label> - <Field.Row> - <Controller - name='statusType' - control={control} - rules={{ required: true }} - render={({ field }): ReactElement => <Select {...field} placeholder={t('Presence')} options={presenceOptions} />} - /> - </Field.Row> - {errors?.statusType && <Field.Error>{t('error-the-field-is-required', { field: t('Presence') })}</Field.Error>} - </Field> - <Field> - <Field.Row> - <ButtonGroup stretch w='full'> - <Button onClick={onClose}>{t('Cancel')}</Button> - <Button primary type='submit' disabled={!isDirty}> - {t('Save')} - </Button> - </ButtonGroup> - </Field.Row> - </Field> - {_id && ( + <> + <ContextualbarScrollableContent> + <FieldGroup id={formId} is='form' onSubmit={handleSubmit(handleSave)}> + <Field> + <Field.Label>{t('Name')}</Field.Label> + <Field.Row> + <TextInput {...register('name', { required: true })} placeholder={t('Name')} /> + </Field.Row> + {errors?.name && <Field.Error>{t('error-the-field-is-required', { field: t('Name') })}</Field.Error>} + </Field> <Field> + <Field.Label>{t('Presence')}</Field.Label> <Field.Row> - <ButtonGroup stretch w='full'> - <Button danger onClick={handleDeleteStatus}> - <Icon name='trash' mie='x4' /> - {t('Delete')} - </Button> - </ButtonGroup> + <Controller + name='statusType' + control={control} + rules={{ required: true }} + render={({ field }): ReactElement => <Select {...field} placeholder={t('Presence')} options={presenceOptions} />} + /> </Field.Row> + {errors?.statusType && <Field.Error>{t('error-the-field-is-required', { field: t('Presence') })}</Field.Error>} </Field> + </FieldGroup> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button onClick={onClose}>{t('Cancel')}</Button> + <Button form={formId} primary type='submit' disabled={!isDirty}> + {t('Save')} + </Button> + </ButtonGroup> + {_id && ( + <ButtonGroup mbs='x8' stretch> + <Button danger onClick={handleDeleteStatus}> + <Icon name='trash' mie='x4' /> + {t('Delete')} + </Button> + </ButtonGroup> )} - </FieldGroup> - </ContextualbarScrollableContent> + </ContextualbarFooter> + </> ); }; diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index f8dd530f17ed..8f91054ebdf7 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -7,7 +7,7 @@ import type { ReactElement } from 'react'; import React, { useState, useMemo } from 'react'; import { RoomSettingsEnum } from '../../../../definition/IRoomTypeConfig'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import GenericModal from '../../../components/GenericModal'; import RoomAvatarEditor from '../../../components/avatar/RoomAvatarEditor'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; @@ -208,156 +208,152 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => }); return ( - <ContextualbarScrollableContent is='form' onSubmit={useMutableCallback((e) => e.preventDefault())}> - {room.t !== 'd' && ( - <Box pbe='x24' display='flex' justifyContent='center'> - <RoomAvatarEditor disabled={isRoomFederated(room)} roomAvatar={roomAvatar} room={room} onChangeAvatar={handleRoomAvatar} /> - </Box> - )} - <Field> - <Field.Label>{t('Name')}</Field.Label> - <Field.Row> - <TextInput disabled={deleting || !canViewName} value={roomName} onChange={handleRoomName} flexGrow={1} /> - </Field.Row> - </Field> - {room.t !== 'd' && ( - <> - {room.u && ( - <Field> - <Field.Label>{t('Owner')}</Field.Label> - <Field.Row> - <Box fontScale='p2'>{room.u?.username}</Box> - </Field.Row> - </Field> - )} - {canViewDescription && ( - <Field> - <Field.Label>{t('Description')}</Field.Label> - <Field.Row> - <TextAreaInput - rows={4} - disabled={deleting || isRoomFederated(room)} - value={roomDescription} - onChange={handleRoomDescription} - flexGrow={1} - /> - </Field.Row> - </Field> - )} - {canViewAnnouncement && ( - <Field> - <Field.Label>{t('Announcement')}</Field.Label> - <Field.Row> - <TextAreaInput - rows={4} - disabled={deleting || isRoomFederated(room)} - value={roomAnnouncement} - onChange={handleRoomAnnouncement} - flexGrow={1} - /> - </Field.Row> - </Field> - )} - {canViewTopic && ( - <Field> - <Field.Label>{t('Topic')}</Field.Label> - <Field.Row> - <TextAreaInput rows={4} disabled={deleting} value={roomTopic} onChange={handleRoomTopic} flexGrow={1} /> - </Field.Row> - </Field> - )} - {canViewType && ( - <Field> - <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> - <Field.Label>{t('Private')}</Field.Label> + <> + <ContextualbarScrollableContent is='form' onSubmit={useMutableCallback((e) => e.preventDefault())}> + {room.t !== 'd' && ( + <Box pbe='x24' display='flex' justifyContent='center'> + <RoomAvatarEditor disabled={isRoomFederated(room)} roomAvatar={roomAvatar} room={room} onChangeAvatar={handleRoomAvatar} /> + </Box> + )} + <Field> + <Field.Label>{t('Name')}</Field.Label> + <Field.Row> + <TextInput disabled={deleting || !canViewName} value={roomName} onChange={handleRoomName} flexGrow={1} /> + </Field.Row> + </Field> + {room.t !== 'd' && ( + <> + {room.u && ( + <Field> + <Field.Label>{t('Owner')}</Field.Label> <Field.Row> - <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={roomType === 'p'} onChange={changeRoomType} /> + <Box fontScale='p2'>{room.u?.username}</Box> </Field.Row> - </Box> - <Field.Hint>{t('Just_invited_people_can_access_this_channel')}</Field.Hint> - </Field> - )} - {canViewReadOnly && ( - <Field> - <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> - <Field.Label>{t('Read_only')}</Field.Label> + </Field> + )} + {canViewDescription && ( + <Field> + <Field.Label>{t('Description')}</Field.Label> <Field.Row> - <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={readOnly} onChange={handleReadOnly} /> + <TextAreaInput + rows={4} + disabled={deleting || isRoomFederated(room)} + value={roomDescription} + onChange={handleRoomDescription} + flexGrow={1} + /> </Field.Row> - </Box> - <Field.Hint>{t('Only_authorized_users_can_write_new_messages')}</Field.Hint> - </Field> - )} - {readOnly && ( - <Field> - <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> - <Field.Label>{t('React_when_read_only')}</Field.Label> + </Field> + )} + {canViewAnnouncement && ( + <Field> + <Field.Label>{t('Announcement')}</Field.Label> <Field.Row> - <ToggleSwitch checked={reactWhenReadOnly || isRoomFederated(room)} onChange={handleReactWhenReadOnly} /> + <TextAreaInput + rows={4} + disabled={deleting || isRoomFederated(room)} + value={roomAnnouncement} + onChange={handleRoomAnnouncement} + flexGrow={1} + /> </Field.Row> - </Box> - <Field.Hint>{t('React_when_read_only_changed_successfully')}</Field.Hint> - </Field> - )} - {canViewArchived && ( - <Field> - <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> - <Field.Label>{t('Room_archivation_state_true')}</Field.Label> + </Field> + )} + {canViewTopic && ( + <Field> + <Field.Label>{t('Topic')}</Field.Label> <Field.Row> - <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={archived} onChange={handleArchived} /> + <TextAreaInput rows={4} disabled={deleting} value={roomTopic} onChange={handleRoomTopic} flexGrow={1} /> </Field.Row> - </Box> - </Field> - )} - </> - )} - <Field> - <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> - <Field.Label>{t('Default')}</Field.Label> - <Field.Row> - <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={isDefault} onChange={handleIsDefault} /> - </Field.Row> - </Box> - </Field> - <Field> - <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> - <Field.Label>{t('Favorite')}</Field.Label> - <Field.Row> - <ToggleSwitch disabled={deleting} checked={favorite} onChange={handleFavorite} /> - </Field.Row> - </Box> - </Field> - <Field> - <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> - <Field.Label>{t('Featured')}</Field.Label> - <Field.Row> - <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={featured} onChange={handleFeatured} /> - </Field.Row> - </Box> - </Field> - <Field> - <Field.Row> - <Box display='flex' flexDirection='row' justifyContent='space-between' w='full'> - <ButtonGroup stretch flexGrow={1}> - <Button type='reset' disabled={!hasUnsavedChanges || deleting} onClick={reset}> - {t('Reset')} - </Button> - <Button flexGrow={1} disabled={!hasUnsavedChanges || deleting} onClick={handleSave}> - {t('Save')} - </Button> - </ButtonGroup> + </Field> + )} + {canViewType && ( + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('Private')}</Field.Label> + <Field.Row> + <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={roomType === 'p'} onChange={changeRoomType} /> + </Field.Row> + </Box> + <Field.Hint>{t('Just_invited_people_can_access_this_channel')}</Field.Hint> + </Field> + )} + {canViewReadOnly && ( + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('Read_only')}</Field.Label> + <Field.Row> + <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={readOnly} onChange={handleReadOnly} /> + </Field.Row> + </Box> + <Field.Hint>{t('Only_authorized_users_can_write_new_messages')}</Field.Hint> + </Field> + )} + {readOnly && ( + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('React_when_read_only')}</Field.Label> + <Field.Row> + <ToggleSwitch checked={reactWhenReadOnly || isRoomFederated(room)} onChange={handleReactWhenReadOnly} /> + </Field.Row> + </Box> + <Field.Hint>{t('React_when_read_only_changed_successfully')}</Field.Hint> + </Field> + )} + {canViewArchived && ( + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('Room_archivation_state_true')}</Field.Label> + <Field.Row> + <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={archived} onChange={handleArchived} /> + </Field.Row> + </Box> + </Field> + )} + </> + )} + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('Default')}</Field.Label> + <Field.Row> + <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={isDefault} onChange={handleIsDefault} /> + </Field.Row> + </Box> + </Field> + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('Favorite')}</Field.Label> + <Field.Row> + <ToggleSwitch disabled={deleting} checked={favorite} onChange={handleFavorite} /> + </Field.Row> </Box> - </Field.Row> - </Field> - <Field> - <Field.Row> - <Button flexGrow={1} danger disabled={deleting || !canDelete || isRoomFederated(room)} onClick={handleDelete}> + </Field> + <Field> + <Box display='flex' flexDirection='row' justifyContent='space-between' flexGrow={1}> + <Field.Label>{t('Featured')}</Field.Label> + <Field.Row> + <ToggleSwitch disabled={deleting || isRoomFederated(room)} checked={featured} onChange={handleFeatured} /> + </Field.Row> + </Box> + </Field> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button type='reset' disabled={!hasUnsavedChanges || deleting} onClick={reset}> + {t('Reset')} + </Button> + <Button disabled={!hasUnsavedChanges || deleting} onClick={handleSave}> + {t('Save')} + </Button> + </ButtonGroup> + <ButtonGroup mbs='x8' stretch> + <Button danger disabled={deleting || !canDelete || isRoomFederated(room)} onClick={handleDelete}> <Icon name='trash' size='x16' /> {t('Delete')} </Button> - </Field.Row> - </Field> - </ContextualbarScrollableContent> + </ButtonGroup> + </ContextualbarFooter> + </> ); }; diff --git a/apps/meteor/client/views/admin/users/AddUser.js b/apps/meteor/client/views/admin/users/AddUser.js index e7d439317cb0..1260ededbb0b 100644 --- a/apps/meteor/client/views/admin/users/AddUser.js +++ b/apps/meteor/client/views/admin/users/AddUser.js @@ -1,10 +1,11 @@ -import { Field, Box, Button } from '@rocket.chat/fuselage'; +import { ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useRoute, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; import { useQuery } from '@tanstack/react-query'; import React, { useMemo, useCallback, useState } from 'react'; import { parseCSV } from '../../../../lib/utils/parseCSV'; +import { ContextualbarFooter } from '../../../components/Contextualbar'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; import { useForm } from '../../../hooks/useForm'; import UserForm from './UserForm'; @@ -119,18 +120,16 @@ const AddUser = ({ onReload, ...props }) => { const append = useMemo( () => ( - <Field> - <Field.Row> - <Box display='flex' flexDirection='row' justifyContent='space-between' w='full'> - <Button flexGrow={1} disabled={!hasUnsavedChanges} onClick={reset} mie='x4'> - {t('Cancel')} - </Button> - <Button primary flexGrow={1} disabled={!hasUnsavedChanges} onClick={handleSave}> - {t('Save')} - </Button> - </Box> - </Field.Row> - </Field> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button disabled={!hasUnsavedChanges} onClick={reset}> + {t('Cancel')} + </Button> + <Button primary disabled={!hasUnsavedChanges} onClick={handleSave}> + {t('Save')} + </Button> + </ButtonGroup> + </ContextualbarFooter> ), [hasUnsavedChanges, reset, t, handleSave], ); diff --git a/apps/meteor/client/views/admin/users/EditUser.js b/apps/meteor/client/views/admin/users/EditUser.js index ae216dd4114a..59e37674685b 100644 --- a/apps/meteor/client/views/admin/users/EditUser.js +++ b/apps/meteor/client/views/admin/users/EditUser.js @@ -1,8 +1,9 @@ -import { Box, Field, Margins, Button } from '@rocket.chat/fuselage'; +import { ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo, useState, useCallback } from 'react'; +import { ContextualbarFooter } from '../../../components/Contextualbar'; import UserAvatarEditor from '../../../components/avatar/UserAvatarEditor'; import { useEndpointAction } from '../../../hooks/useEndpointAction'; import { useEndpointUpload } from '../../../hooks/useEndpointUpload'; @@ -127,20 +128,16 @@ function EditUser({ data, roles, onReload, ...props }) { const append = useMemo( () => ( - <Field> - <Field.Row> - <Box display='flex' flexDirection='row' justifyContent='space-between' w='full'> - <Margins inlineEnd='x4'> - <Button flexGrow={1} type='reset' disabled={!canSaveOrReset} onClick={reset}> - {t('Reset')} - </Button> - <Button primary mie='none' flexGrow={1} disabled={!canSaveOrReset} onClick={handleSave}> - {t('Save')} - </Button> - </Margins> - </Box> - </Field.Row> - </Field> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button type='reset' disabled={!canSaveOrReset} onClick={reset}> + {t('Reset')} + </Button> + <Button primary disabled={!canSaveOrReset} onClick={handleSave}> + {t('Save')} + </Button> + </ButtonGroup> + </ContextualbarFooter> ), [handleSave, canSaveOrReset, reset, t], ); diff --git a/apps/meteor/client/views/admin/users/InviteUsers.tsx b/apps/meteor/client/views/admin/users/InviteUsers.tsx index 70b9878bd989..cad5e8c93041 100644 --- a/apps/meteor/client/views/admin/users/InviteUsers.tsx +++ b/apps/meteor/client/views/admin/users/InviteUsers.tsx @@ -1,10 +1,21 @@ -import { Box, Button, Icon, States, StatesAction, StatesActions, StatesSubtitle, StatesTitle, TextAreaInput } from '@rocket.chat/fuselage'; +import { + Box, + Button, + ButtonGroup, + Icon, + States, + StatesAction, + StatesActions, + StatesSubtitle, + StatesTitle, + TextAreaInput, +} from '@rocket.chat/fuselage'; import { useTranslation, useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement, ChangeEvent, ComponentProps } from 'react'; import React, { useCallback, useState } from 'react'; import { validateEmail } from '../../../../lib/emailValidator'; -import { ContextualbarScrollableContent } from '../../../components/Contextualbar'; +import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../components/Contextualbar'; import { useSendInvitationEmailMutation } from './hooks/useSendInvitationEmailMutation'; import { useSmtpConfig } from './hooks/useSmtpConfig'; @@ -39,18 +50,24 @@ const InviteUsers = (props: InviteUsersProps): ReactElement => { } return ( - <ContextualbarScrollableContent {...props} color='default'> - <Box is='h2' fontScale='h2' mb='x8'> - {t('Send_invitation_email')} - </Box> - <Box fontScale='p2' mb='x8'> - {t('Send_invitation_email_info')} - </Box> - <TextAreaInput rows={5} flexGrow={0} onChange={(e: ChangeEvent<HTMLInputElement>): void => setText(e.currentTarget.value)} /> - <Button primary onClick={handleClick} disabled={!getEmails(text).length} alignItems='stretch' mb='x8'> - <Icon name='send' size='x16' /> {t('Send')} - </Button> - </ContextualbarScrollableContent> + <> + <ContextualbarScrollableContent {...props} color='default'> + <Box is='h2' fontScale='h2' mb='x8'> + {t('Send_invitation_email')} + </Box> + <Box fontScale='p2' mb='x8'> + {t('Send_invitation_email_info')} + </Box> + <TextAreaInput rows={5} flexGrow={0} onChange={(e: ChangeEvent<HTMLInputElement>): void => setText(e.currentTarget.value)} /> + </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button primary onClick={handleClick} disabled={!getEmails(text).length} alignItems='stretch' mb='x8'> + <Icon name='send' size='x16' /> {t('Send')} + </Button> + </ButtonGroup> + </ContextualbarFooter> + </> ); }; diff --git a/apps/meteor/client/views/admin/users/UserForm.js b/apps/meteor/client/views/admin/users/UserForm.js index b0ce0cfdfbf6..696ed87d28dc 100644 --- a/apps/meteor/client/views/admin/users/UserForm.js +++ b/apps/meteor/client/views/admin/users/UserForm.js @@ -68,213 +68,162 @@ export default function UserForm({ formValues, formHandlers, availableRoles, app }, [watch, handleCustomFields]); return ( - <ContextualbarScrollableContent {...props} is='form' onSubmit={useCallback((e) => e.preventDefault(), [])} autoComplete='off'> - <FieldGroup> - {prepend} - {useMemo( - () => ( - <Field> - <Field.Label>{t('Name')}</Field.Label> - <Field.Row> - <TextInput error={errors && errors.name} flexGrow={1} value={name} onChange={handleName} /> - </Field.Row> - {errors && errors.name && <Field.Error>{errors.name}</Field.Error>} - </Field> - ), - [t, name, handleName, errors], - )} - {useMemo( - () => ( - <Field> - <Field.Label>{t('Username')}</Field.Label> - <Field.Row> - <TextInput - error={errors && errors.username} - flexGrow={1} - value={username} - onChange={handleUsername} - addon={<Icon name='at' size='x20' />} - /> - </Field.Row> - {errors && errors.username && <Field.Error>{errors.username}</Field.Error>} - </Field> - ), - [t, username, handleUsername, errors], - )} - {useMemo( - () => ( - <Field> - <Field.Label>{t('Email')}</Field.Label> - <Field.Row> - <TextInput - error={errors && errors.email} - flexGrow={1} - value={email} - error={!validateEmail(email) && email.length > 0 ? 'error' : undefined} - onChange={handleEmail} - addon={<Icon name='mail' size='x20' />} - /> - </Field.Row> - {errors && errors.email && <Field.Error>{errors.email}</Field.Error>} - <Field.Row> - <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between' mbs='x4'> - <Box color='default' fontScale='p2m'> - {t('Verified')} - </Box> - <ToggleSwitch checked={verified} onChange={handleVerified} /> - </Box> - </Field.Row> - </Field> - ), - [t, email, handleEmail, verified, handleVerified, errors], - )} - {useMemo( - () => ( - <Field> - <Field.Label>{t('StatusMessage')}</Field.Label> - <Field.Row> - <TextInput flexGrow={1} value={statusText} onChange={handleStatusText} addon={<Icon name='edit' size='x20' />} /> - </Field.Row> - </Field> - ), - [t, statusText, handleStatusText], - )} - {useMemo( - () => ( - <Field> - <Field.Label>{t('Bio')}</Field.Label> - <Field.Row> - <TextAreaInput - rows={3} - flexGrow={1} - value={bio} - onChange={handleBio} - addon={<Icon name='edit' size='x20' alignSelf='center' />} - /> - </Field.Row> - </Field> - ), - [bio, handleBio, t], - )} - {useMemo( - () => ( - <Field> - <Field.Label>{t('Nickname')}</Field.Label> - <Field.Row> - <TextInput - flexGrow={1} - value={nickname} - onChange={handleNickname} - addon={<Icon name='edit' size='x20' alignSelf='center' />} - /> - </Field.Row> - </Field> - ), - [nickname, handleNickname, t], - )} - </FieldGroup> - - <FieldGroup is='form' onSubmit={useCallback((e) => e.preventDefault(), [])} autoComplete='off'> - {useMemo( - () => - !setRandomPassword && ( + <> + <ContextualbarScrollableContent {...props} is='form' onSubmit={useCallback((e) => e.preventDefault(), [])} autoComplete='off'> + <FieldGroup> + {prepend} + {useMemo( + () => ( + <Field> + <Field.Label>{t('Name')}</Field.Label> + <Field.Row> + <TextInput error={errors && errors.name} flexGrow={1} value={name} onChange={handleName} /> + </Field.Row> + {errors && errors.name && <Field.Error>{errors.name}</Field.Error>} + </Field> + ), + [t, name, handleName, errors], + )} + {useMemo( + () => ( <Field> - <Field.Label>{t('Password')}</Field.Label> + <Field.Label>{t('Username')}</Field.Label> <Field.Row> - <PasswordInput - errors={errors && errors.password} + <TextInput + error={errors && errors.username} flexGrow={1} - value={password} - onChange={handlePassword} - addon={<Icon name='key' size='x20' />} - autoComplete='new-password' + value={username} + onChange={handleUsername} + addon={<Icon name='at' size='x20' />} /> </Field.Row> - {errors && errors.password && <Field.Error>{errors.password}</Field.Error>} + {errors && errors.username && <Field.Error>{errors.username}</Field.Error>} </Field> ), - [t, password, handlePassword, errors, setRandomPassword], - )} - {useMemo( - () => ( - <Field> - <Field.Row> - <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between'> - <Box color='default' fontScale='p2m'> - {t('Require_password_change')} - </Box> - <ToggleSwitch - disabled={setRandomPassword} - checked={setRandomPassword || requirePasswordChange} - onChange={handleRequirePasswordChange} + [t, username, handleUsername, errors], + )} + {useMemo( + () => ( + <Field> + <Field.Label>{t('Email')}</Field.Label> + <Field.Row> + <TextInput + error={errors && errors.email} + flexGrow={1} + value={email} + error={!validateEmail(email) && email.length > 0 ? 'error' : undefined} + onChange={handleEmail} + addon={<Icon name='mail' size='x20' />} /> - </Box> - </Field.Row> - </Field> - ), - [t, setRandomPassword, requirePasswordChange, handleRequirePasswordChange], - )} - {useMemo( - () => ( - <Field> - <Field.Row> - <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between'> - <Box color='default' fontScale='p2m'> - {t('Set_random_password_and_send_by_email')} + </Field.Row> + {errors && errors.email && <Field.Error>{errors.email}</Field.Error>} + <Field.Row> + <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between' mbs='x4'> + <Box color='default' fontScale='p2m'> + {t('Verified')} + </Box> + <ToggleSwitch checked={verified} onChange={handleVerified} /> </Box> - <ToggleSwitch checked={setRandomPassword} disabled={!isSmtpEnabled} onChange={handleSetRandomPassword} /> - </Box> - </Field.Row> - {!isSmtpEnabled && ( - <Field.Hint dangerouslySetInnerHTML={{ __html: t('Send_Email_SMTP_Warning', { url: 'admin/settings/Email' }) }} /> - )} - </Field> - ), - [t, setRandomPassword, handleSetRandomPassword, isSmtpEnabled], - )} - {useMemo( - () => ( - <Field> - <Field.Label>{t('Roles')}</Field.Label> - <Field.Row> - <MultiSelectFiltered - options={availableRoles} - value={roles} - onChange={handleRoles} - placeholder={t('Select_role')} - flexShrink={1} - /> - </Field.Row> - </Field> - ), - [availableRoles, handleRoles, roles, t], - )} - {useMemo( - () => - handleJoinDefaultChannels && ( + </Field.Row> + </Field> + ), + [t, email, handleEmail, verified, handleVerified, errors], + )} + {useMemo( + () => ( + <Field> + <Field.Label>{t('StatusMessage')}</Field.Label> + <Field.Row> + <TextInput flexGrow={1} value={statusText} onChange={handleStatusText} addon={<Icon name='edit' size='x20' />} /> + </Field.Row> + </Field> + ), + [t, statusText, handleStatusText], + )} + {useMemo( + () => ( + <Field> + <Field.Label>{t('Bio')}</Field.Label> + <Field.Row> + <TextAreaInput + rows={3} + flexGrow={1} + value={bio} + onChange={handleBio} + addon={<Icon name='edit' size='x20' alignSelf='center' />} + /> + </Field.Row> + </Field> + ), + [bio, handleBio, t], + )} + {useMemo( + () => ( + <Field> + <Field.Label>{t('Nickname')}</Field.Label> + <Field.Row> + <TextInput + flexGrow={1} + value={nickname} + onChange={handleNickname} + addon={<Icon name='edit' size='x20' alignSelf='center' />} + /> + </Field.Row> + </Field> + ), + [nickname, handleNickname, t], + )} + </FieldGroup> + + <FieldGroup is='form' onSubmit={useCallback((e) => e.preventDefault(), [])} autoComplete='off'> + {useMemo( + () => + !setRandomPassword && ( + <Field> + <Field.Label>{t('Password')}</Field.Label> + <Field.Row> + <PasswordInput + errors={errors && errors.password} + flexGrow={1} + value={password} + onChange={handlePassword} + addon={<Icon name='key' size='x20' />} + autoComplete='new-password' + /> + </Field.Row> + {errors && errors.password && <Field.Error>{errors.password}</Field.Error>} + </Field> + ), + [t, password, handlePassword, errors, setRandomPassword], + )} + {useMemo( + () => ( <Field> <Field.Row> <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between'> <Box color='default' fontScale='p2m'> - {t('Join_default_channels')} + {t('Require_password_change')} </Box> - <ToggleSwitch checked={joinDefaultChannels} onChange={handleJoinDefaultChannels} /> + <ToggleSwitch + disabled={setRandomPassword} + checked={setRandomPassword || requirePasswordChange} + onChange={handleRequirePasswordChange} + /> </Box> </Field.Row> </Field> ), - [handleJoinDefaultChannels, t, joinDefaultChannels], - )} - {useMemo( - () => - handleSendWelcomeEmail && ( + [t, setRandomPassword, requirePasswordChange, handleRequirePasswordChange], + )} + {useMemo( + () => ( <Field> <Field.Row> <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between'> <Box color='default' fontScale='p2m'> - {t('Send_welcome_email')} + {t('Set_random_password_and_send_by_email')} </Box> - <ToggleSwitch checked={sendWelcomeEmail} onChange={handleSendWelcomeEmail} disabled={!isSmtpEnabled} /> + <ToggleSwitch checked={setRandomPassword} disabled={!isSmtpEnabled} onChange={handleSetRandomPassword} /> </Box> </Field.Row> {!isSmtpEnabled && ( @@ -282,21 +231,74 @@ export default function UserForm({ formValues, formHandlers, availableRoles, app )} </Field> ), - [handleSendWelcomeEmail, t, sendWelcomeEmail, isSmtpEnabled], - )} - {useMemo( - () => - customFieldsMetadata && ( - <> - <Divider /> - <Box fontScale='h4'>{t('Custom_Fields')}</Box> - <CustomFieldsForm formName='customFields' formControl={control} metadata={customFieldsMetadata} /> - </> + [t, setRandomPassword, handleSetRandomPassword, isSmtpEnabled], + )} + {useMemo( + () => ( + <Field> + <Field.Label>{t('Roles')}</Field.Label> + <Field.Row> + <MultiSelectFiltered + options={availableRoles} + value={roles} + onChange={handleRoles} + placeholder={t('Select_role')} + flexShrink={1} + /> + </Field.Row> + </Field> ), - [customFieldsMetadata, control, t], - )} - {append} - </FieldGroup> - </ContextualbarScrollableContent> + [availableRoles, handleRoles, roles, t], + )} + {useMemo( + () => + handleJoinDefaultChannels && ( + <Field> + <Field.Row> + <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between'> + <Box color='default' fontScale='p2m'> + {t('Join_default_channels')} + </Box> + <ToggleSwitch checked={joinDefaultChannels} onChange={handleJoinDefaultChannels} /> + </Box> + </Field.Row> + </Field> + ), + [handleJoinDefaultChannels, t, joinDefaultChannels], + )} + {useMemo( + () => + handleSendWelcomeEmail && ( + <Field> + <Field.Row> + <Box flexGrow={1} display='flex' flexDirection='row' alignItems='center' justifyContent='space-between'> + <Box color='default' fontScale='p2m'> + {t('Send_welcome_email')} + </Box> + <ToggleSwitch checked={sendWelcomeEmail} onChange={handleSendWelcomeEmail} disabled={!isSmtpEnabled} /> + </Box> + </Field.Row> + {!isSmtpEnabled && ( + <Field.Hint dangerouslySetInnerHTML={{ __html: t('Send_Email_SMTP_Warning', { url: 'admin/settings/Email' }) }} /> + )} + </Field> + ), + [handleSendWelcomeEmail, t, sendWelcomeEmail, isSmtpEnabled], + )} + {useMemo( + () => + customFieldsMetadata && ( + <> + <Divider /> + <Box fontScale='h4'>{t('Custom_Fields')}</Box> + <CustomFieldsForm formName='customFields' formControl={control} metadata={customFieldsMetadata} /> + </> + ), + [customFieldsMetadata, control, t], + )} + </FieldGroup> + </ContextualbarScrollableContent> + {append} + </> ); } diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js index 90234643f353..0b46f7077902 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js @@ -37,6 +37,7 @@ import { ContextualbarTitle, ContextualbarClose, ContextualbarScrollableContent, + ContextualbarFooter, } from '../../../../../components/Contextualbar'; import GenericModal from '../../../../../components/GenericModal'; import RawText from '../../../../../components/RawText'; @@ -304,7 +305,6 @@ function EditChannel({ room, onClickClose, onClickBack }) { <ContextualbarTitle>{room.teamId ? t('edit-team') : t('edit-room')}</ContextualbarTitle> {onClickClose && <ContextualbarClose onClick={onClickClose} />} </ContextualbarHeader> - <ContextualbarScrollableContent p='x24' is='form' onSubmit={useMutableCallback((e) => e.preventDefault())}> <Box display='flex' justifyContent='center'> <RoomAvatarEditor room={room} roomAvatar={roomAvatar} onChangeAvatar={handleRoomAvatar} /> @@ -489,29 +489,23 @@ function EditChannel({ room, onClickClose, onClickBack }) { </Accordion.Item> </Accordion> )} - <Field> - <Field.Row> - <Box display='flex' flexDirection='row' justifyContent='space-between' w='full'> - <ButtonGroup stretch flexGrow={1}> - <Button type='reset' disabled={!hasUnsavedChanges} onClick={reset}> - {t('Reset')} - </Button> - <Button flexGrow={1} disabled={!hasUnsavedChanges} onClick={handleSave}> - {t('Save')} - </Button> - </ButtonGroup> - </Box> - </Field.Row> - </Field> - <Field> - <Field.Row> - <Button flexGrow={1} danger disabled={!canDelete || isFederated} onClick={handleDelete}> - <Icon name='trash' size='x16' /> - {t('Delete')} - </Button> - </Field.Row> - </Field> </ContextualbarScrollableContent> + <ContextualbarFooter> + <ButtonGroup stretch> + <Button type='reset' disabled={!hasUnsavedChanges} onClick={reset}> + {t('Reset')} + </Button> + <Button disabled={!hasUnsavedChanges} onClick={handleSave}> + {t('Save')} + </Button> + </ButtonGroup> + <ButtonGroup stretch mbs='x8'> + <Button danger disabled={!canDelete || isFederated} onClick={handleDelete}> + <Icon name='trash' size='x16' /> + {t('Delete')} + </Button> + </ButtonGroup> + </ContextualbarFooter> </> ); } From 6f820d5fc03b4d2eb28a250db425c6d84804429e Mon Sep 17 00:00:00 2001 From: Tasso Evangelista <tasso.evangelista@rocket.chat> Date: Tue, 18 Jul 2023 13:45:04 -0300 Subject: [PATCH 122/149] refactor: Spread REST endpoints and methods types across EE code (#29849) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/meteor/client/main.ts | 1 - .../api-enterprise/server/canned-responses.ts | 36 ++++++ .../server/api/business-hours.ts | 16 +++ .../livechat-enterprise/server/api/units.ts | 21 ++++ .../CannedResponseEditWithData.tsx | 1 - apps/meteor/ee/definition/.eslintrc.json | 3 - apps/meteor/ee/definition/index.ts | 2 - apps/meteor/ee/definition/methods/index.ts | 1 - apps/meteor/ee/definition/methods/license.ts | 10 -- apps/meteor/ee/definition/rest/index.ts | 7 -- apps/meteor/ee/definition/rest/v1/chat.ts | 34 ------ .../definition/rest/v1/engagementDashboard.ts | 111 ------------------ .../rest/v1/omnichannel/businessHours.ts | 15 --- .../rest/v1/omnichannel/businessUnits.ts | 25 ---- .../rest/v1/omnichannel/cannedResponses.ts | 36 ------ .../definition/rest/v1/omnichannel/index.ts | 3 - apps/meteor/ee/definition/rest/v1/roles.ts | 84 ------------- .../rest/v1/sessions/SessionsPaginateProps.ts | 28 ----- .../rest/v1/sessions/SessionsProps.ts | 18 --- .../ee/definition/rest/v1/sessions/index.ts | 4 - .../definition/rest/v1/sessions/sessions.ts | 29 ----- apps/meteor/ee/server/api/chat.ts | 16 +++ .../api/engagementDashboard/channels.ts | 27 +++++ .../api/engagementDashboard/messages.ts | 38 ++++++ .../server/api/engagementDashboard/users.ts | 60 ++++++++++ apps/meteor/ee/server/api/roles.ts | 85 +++++++++++++- apps/meteor/ee/server/api/sessions.ts | 70 ++++++++++- 27 files changed, 366 insertions(+), 415 deletions(-) delete mode 100644 apps/meteor/ee/definition/.eslintrc.json delete mode 100644 apps/meteor/ee/definition/index.ts delete mode 100644 apps/meteor/ee/definition/methods/index.ts delete mode 100644 apps/meteor/ee/definition/methods/license.ts delete mode 100644 apps/meteor/ee/definition/rest/index.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/chat.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/engagementDashboard.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/omnichannel/index.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/roles.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/index.ts delete mode 100644 apps/meteor/ee/definition/rest/v1/sessions/sessions.ts diff --git a/apps/meteor/client/main.ts b/apps/meteor/client/main.ts index ed98c06c3297..6bcf2789e819 100644 --- a/apps/meteor/client/main.ts +++ b/apps/meteor/client/main.ts @@ -1,4 +1,3 @@ -import '../ee/definition'; import '../ee/client/ecdh'; import './polyfills'; diff --git a/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts b/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts index 676c3e5fe6a5..55a5cd55fbe3 100644 --- a/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts +++ b/apps/meteor/ee/app/api-enterprise/server/canned-responses.ts @@ -1,10 +1,46 @@ import { Meteor } from 'meteor/meteor'; import { isPOSTCannedResponsesProps, isDELETECannedResponsesProps, isCannedResponsesProps } from '@rocket.chat/rest-typings'; +import type { ILivechatDepartment, IOmnichannelCannedResponse, IUser } from '@rocket.chat/core-typings'; +import type { PaginatedResult, PaginatedRequest } from '@rocket.chat/rest-typings'; import { API } from '../../../../app/api/server'; import { findAllCannedResponses, findAllCannedResponsesFilter, findOneCannedResponse } from './lib/canned-responses'; import { getPaginationItems } from '../../../../app/api/server/helpers/getPaginationItems'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/canned-responses': { + GET: ( + params: PaginatedRequest<{ + shortcut?: string; + text?: string; + scope?: string; + createdBy?: IUser['username']; + tags?: any; + departmentId?: ILivechatDepartment['_id']; + }>, + ) => PaginatedResult<{ + cannedResponses: IOmnichannelCannedResponse[]; + }>; + POST: (params: { + _id?: IOmnichannelCannedResponse['_id']; + shortcut: string; + text: string; + scope: string; + tags?: any; + departmentId?: ILivechatDepartment['_id']; + }) => void; + DELETE: (params: { _id: IOmnichannelCannedResponse['_id'] }) => void; + }; + '/v1/canned-responses/:_id': { + GET: () => { + cannedResponse: IOmnichannelCannedResponse; + }; + }; + } +} + API.v1.addRoute( 'canned-responses.get', { authRequired: true, permissionsRequired: ['view-canned-responses'] }, diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts index 15d350798ac9..f0f7ea5646e6 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/business-hours.ts @@ -1,7 +1,23 @@ +import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; + import { API } from '../../../../../app/api/server'; import { getPaginationItems } from '../../../../../app/api/server/helpers/getPaginationItems'; import { findBusinessHours } from '../business-hour/lib/business-hour'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/livechat/business-hours': { + GET: (params: { name?: string; offset: number; count: number; sort: Record<string, unknown> }) => { + businessHours: ILivechatBusinessHour[]; + count: number; + offset: number; + total: number; + }; + }; + } +} + API.v1.addRoute( 'livechat/business-hours', { authRequired: true, permissionsRequired: ['view-livechat-business-hours'] }, diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts index ca7eff11aced..77e39f4780e5 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/units.ts @@ -1,9 +1,30 @@ +import type { ILivechatUnitMonitor, IOmnichannelBusinessUnit } from '@rocket.chat/core-typings'; +import type { PaginatedResult } from '@rocket.chat/rest-typings'; + import { API } from '../../../../../app/api/server'; import { findUnits, findUnitById, findUnitMonitors } from './lib/units'; import { LivechatEnterprise } from '../lib/LivechatEnterprise'; import { findAllDepartmentsAvailable, findAllDepartmentsByUnit } from '../lib/Department'; import { getPaginationItems } from '../../../../../app/api/server/helpers/getPaginationItems'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/livechat/units/:unitId/monitors': { + GET: (params: { unitId: string }) => { monitors: ILivechatUnitMonitor[] }; + }; + '/v1/livechat/units': { + GET: (params: { text: string }) => PaginatedResult & { units: IOmnichannelBusinessUnit[] }; + POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit<IOmnichannelBusinessUnit, '_updatedAt'>; + }; + '/v1/livechat/units/:id': { + GET: () => IOmnichannelBusinessUnit | null; + POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit<IOmnichannelBusinessUnit, '_updatedAt'>; + DELETE: () => number; + }; + } +} + API.v1.addRoute( 'livechat/units/:unitId/monitors', { authRequired: true, permissionsRequired: ['manage-livechat-monitors'] }, diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx index c2295685454a..73e39694adea 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEditWithData.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { FormSkeleton } from '../../../../client/components/Skeleton'; import { AsyncStatePhase } from '../../../../client/hooks/useAsyncState'; import { useEndpointData } from '../../../../client/hooks/useEndpointData'; -import '../../../definition/rest'; import CannedResponseEdit from './CannedResponseEdit'; import CannedResponseEditWithDepartmentData from './CannedResponseEditWithDepartmentData'; diff --git a/apps/meteor/ee/definition/.eslintrc.json b/apps/meteor/ee/definition/.eslintrc.json deleted file mode 100644 index 03828d003760..000000000000 --- a/apps/meteor/ee/definition/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../client/.eslintrc.json" -} diff --git a/apps/meteor/ee/definition/index.ts b/apps/meteor/ee/definition/index.ts deleted file mode 100644 index ea24a13c33ae..000000000000 --- a/apps/meteor/ee/definition/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import './methods'; -import './rest'; diff --git a/apps/meteor/ee/definition/methods/index.ts b/apps/meteor/ee/definition/methods/index.ts deleted file mode 100644 index 3986165b2481..000000000000 --- a/apps/meteor/ee/definition/methods/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './license'; diff --git a/apps/meteor/ee/definition/methods/license.ts b/apps/meteor/ee/definition/methods/license.ts deleted file mode 100644 index 9b5784596f8c..000000000000 --- a/apps/meteor/ee/definition/methods/license.ts +++ /dev/null @@ -1,10 +0,0 @@ -import '@rocket.chat/ui-contexts'; -import type { ILicenseTag } from '../../app/license/definition/ILicenseTag'; - -declare module '@rocket.chat/ui-contexts' { - // eslint-disable-next-line @typescript-eslint/naming-convention - export interface ServerMethods { - 'license:getModules': () => string[]; - 'license:getTags': () => ILicenseTag[]; - } -} diff --git a/apps/meteor/ee/definition/rest/index.ts b/apps/meteor/ee/definition/rest/index.ts deleted file mode 100644 index 1f206f8e8c52..000000000000 --- a/apps/meteor/ee/definition/rest/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// TODO: chapter day backend - move to rest ee definitions to another package - -import './v1/engagementDashboard'; -import './v1/omnichannel'; -import './v1/sessions'; -import './v1/chat'; -import './v1/roles'; diff --git a/apps/meteor/ee/definition/rest/v1/chat.ts b/apps/meteor/ee/definition/rest/v1/chat.ts deleted file mode 100644 index dee7cadc6542..000000000000 --- a/apps/meteor/ee/definition/rest/v1/chat.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { IMessage, ReadReceipt } from '@rocket.chat/core-typings'; -import Ajv from 'ajv'; - -const ajv = new Ajv({ - coerceTypes: true, -}); - -type GetMessageReadReceiptsProps = { - messageId: IMessage['_id']; -}; - -const getMessageReadReceiptsPropsSchema = { - type: 'object', - properties: { - messageId: { - type: 'string', - }, - }, - required: ['messageId'], - additionalProperties: false, -}; - -export const isGetMessageReadReceiptsProps = ajv.compile<GetMessageReadReceiptsProps>(getMessageReadReceiptsPropsSchema); - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/chat.getMessageReadReceipts': { - GET: (params: GetMessageReadReceiptsProps) => { - receipts: ReadReceipt[]; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/engagementDashboard.ts b/apps/meteor/ee/definition/rest/v1/engagementDashboard.ts deleted file mode 100644 index 4a7a504f7731..000000000000 --- a/apps/meteor/ee/definition/rest/v1/engagementDashboard.ts +++ /dev/null @@ -1,111 +0,0 @@ -import type { IDirectMessageRoom, IRoom, IUser } from '@rocket.chat/core-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/engagement-dashboard/channels/list': { - GET: (params: { start: string; end: string; offset?: number; count?: number }) => { - channels: { - room: { - _id: IRoom['_id']; - name: IRoom['name'] | IRoom['fname']; - ts: IRoom['ts']; - t: IRoom['t']; - _updatedAt: IRoom['_updatedAt']; - usernames?: IDirectMessageRoom['usernames']; - }; - messages: number; - lastWeekMessages: number; - diffFromLastWeek: number; - }[]; - count: number; - offset: number; - total: number; - }; - }; - '/v1/engagement-dashboard/messages/origin': { - GET: (params: { start: string; end: string }) => { - origins: { - t: IRoom['t']; - messages: number; - }[]; - }; - }; - '/v1/engagement-dashboard/messages/top-five-popular-channels': { - GET: (params: { start: string; end: string }) => { - channels: { - t: IRoom['t']; - messages: number; - name: IRoom['name'] | IRoom['fname']; - usernames?: IDirectMessageRoom['usernames']; - }[]; - }; - }; - '/v1/engagement-dashboard/messages/messages-sent': { - GET: (params: { start: string; end: string }) => { - days: { day: Date; messages: number }[]; - period: { - count: number; - variation: number; - }; - yesterday: { - count: number; - variation: number; - }; - }; - }; - '/v1/engagement-dashboard/users/active-users': { - GET: (params: { start: string; end: string }) => { - month: { - day: number; - month: number; - year: number; - usersList: IUser['_id'][]; - users: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/chat-busier/weekly-data': { - GET: (params: { start: string }) => { - month: { - users: number; - day: number; - month: number; - year: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/chat-busier/hourly-data': { - GET: (params: { start: string }) => { - hours: { - users: number; - hour: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/users-by-time-of-the-day-in-a-week': { - GET: (params: { start: string; end: string }) => { - week: { - users: number; - hour: number; - day: number; - month: number; - year: number; - }[]; - }; - }; - '/v1/engagement-dashboard/users/new-users': { - GET: (params: { start: string; end: string }) => { - days: { day: Date; users: number }[]; - period: { - count: number; - variation: number; - }; - yesterday: { - count: number; - variation: number; - }; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts deleted file mode 100644 index 4de8be18bda6..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/businessHours.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ILivechatBusinessHour } from '@rocket.chat/core-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/livechat/business-hours': { - GET: (params: { name?: string; offset: number; count: number; sort: Record<string, unknown> }) => { - businessHours: ILivechatBusinessHour[]; - count: number; - offset: number; - total: number; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts deleted file mode 100644 index eff9f042132b..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/businessUnits.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { ILivechatUnitMonitor, IOmnichannelBusinessUnit } from '@rocket.chat/core-typings'; -import type { PaginatedResult } from '@rocket.chat/rest-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/livechat/units.list': { - GET: (params: { text: string }) => PaginatedResult & { - units: IOmnichannelBusinessUnit[]; - }; - }; - '/v1/livechat/units/:unitId/monitors': { - GET: (params: { unitId: string }) => { monitors: ILivechatUnitMonitor[] }; - }; - '/v1/livechat/units': { - GET: (params: { text: string }) => PaginatedResult & { units: IOmnichannelBusinessUnit[] }; - POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit<IOmnichannelBusinessUnit, '_updatedAt'>; - }; - '/v1/livechat/units/:id': { - GET: () => IOmnichannelBusinessUnit | null; - POST: (params: { unitData: string; unitMonitors: string; unitDepartments: string }) => Omit<IOmnichannelBusinessUnit, '_updatedAt'>; - DELETE: () => number; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts deleted file mode 100644 index f2c0fd042bfb..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/cannedResponses.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { ILivechatDepartment, IOmnichannelCannedResponse, IUser } from '@rocket.chat/core-typings'; -import type { PaginatedResult, PaginatedRequest } from '@rocket.chat/rest-typings'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/canned-responses': { - GET: ( - params: PaginatedRequest<{ - shortcut?: string; - text?: string; - scope?: string; - createdBy?: IUser['username']; - tags?: any; - departmentId?: ILivechatDepartment['_id']; - }>, - ) => PaginatedResult<{ - cannedResponses: IOmnichannelCannedResponse[]; - }>; - POST: (params: { - _id?: IOmnichannelCannedResponse['_id']; - shortcut: string; - text: string; - scope: string; - tags?: any; - departmentId?: ILivechatDepartment['_id']; - }) => void; - DELETE: (params: { _id: IOmnichannelCannedResponse['_id'] }) => void; - }; - '/v1/canned-responses/:_id': { - GET: () => { - cannedResponse: IOmnichannelCannedResponse; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/omnichannel/index.ts b/apps/meteor/ee/definition/rest/v1/omnichannel/index.ts deleted file mode 100644 index 324b4087895a..000000000000 --- a/apps/meteor/ee/definition/rest/v1/omnichannel/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import './businessHours'; -import './businessUnits'; -import './cannedResponses'; diff --git a/apps/meteor/ee/definition/rest/v1/roles.ts b/apps/meteor/ee/definition/rest/v1/roles.ts deleted file mode 100644 index 49ea8c5d929e..000000000000 --- a/apps/meteor/ee/definition/rest/v1/roles.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type { IRole } from '@rocket.chat/core-typings'; -import Ajv from 'ajv'; - -const ajv = new Ajv({ - coerceTypes: true, -}); - -type RoleCreateProps = Pick<IRole, 'name'> & Partial<Pick<IRole, 'description' | 'scope' | 'mandatory2fa'>>; - -const roleCreatePropsSchema = { - type: 'object', - properties: { - name: { - type: 'string', - }, - description: { - type: 'string', - nullable: true, - }, - scope: { - type: 'string', - enum: ['Users', 'Subscriptions'], - nullable: true, - }, - mandatory2fa: { - type: 'boolean', - nullable: true, - }, - }, - required: ['name'], - additionalProperties: false, -}; - -export const isRoleCreateProps = ajv.compile<RoleCreateProps>(roleCreatePropsSchema); - -type RoleUpdateProps = { - roleId: IRole['_id']; - name: IRole['name']; -} & Partial<RoleCreateProps>; - -const roleUpdatePropsSchema = { - type: 'object', - properties: { - roleId: { - type: 'string', - }, - name: { - type: 'string', - }, - description: { - type: 'string', - nullable: true, - }, - scope: { - type: 'string', - enum: ['Users', 'Subscriptions'], - nullable: true, - }, - mandatory2fa: { - type: 'boolean', - nullable: true, - }, - }, - required: ['roleId', 'name'], - additionalProperties: false, -}; - -export const isRoleUpdateProps = ajv.compile<RoleUpdateProps>(roleUpdatePropsSchema); - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/roles.create': { - POST: (params: RoleCreateProps) => { - role: IRole; - }; - }; - '/v1/roles.update': { - POST: (role: RoleUpdateProps) => { - role: IRole; - }; - }; - } -} diff --git a/apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts b/apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts deleted file mode 100644 index 20a7b145ee01..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/SessionsPaginateProps.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { PaginatedRequest } from '@rocket.chat/rest-typings'; -import Ajv from 'ajv'; - -const ajv = new Ajv({ coerceTypes: true }); - -export type SessionsPaginateProps = PaginatedRequest<{ - filter?: string; -}>; - -export const isSessionsPaginateProps = ajv.compile<SessionsPaginateProps>({ - type: 'object', - properties: { - offset: { - type: 'number', - }, - count: { - type: 'number', - }, - filter: { - type: 'string', - }, - sort: { - type: 'string', - }, - }, - required: [], - additionalProperties: false, -}); diff --git a/apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts b/apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts deleted file mode 100644 index e014db56a9e7..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/SessionsProps.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Ajv from 'ajv'; - -const ajv = new Ajv({ coerceTypes: true }); - -export type SessionsProps = { - sessionId: string; -}; - -export const isSessionsProps = ajv.compile<SessionsProps>({ - type: 'object', - properties: { - sessionId: { - type: 'string', - }, - }, - required: ['sessionId'], - additionalProperties: false, -}); diff --git a/apps/meteor/ee/definition/rest/v1/sessions/index.ts b/apps/meteor/ee/definition/rest/v1/sessions/index.ts deleted file mode 100644 index 445c557a74cf..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import './sessions'; - -export * from './SessionsProps'; -export * from './SessionsPaginateProps'; diff --git a/apps/meteor/ee/definition/rest/v1/sessions/sessions.ts b/apps/meteor/ee/definition/rest/v1/sessions/sessions.ts deleted file mode 100644 index 8f006e437af5..000000000000 --- a/apps/meteor/ee/definition/rest/v1/sessions/sessions.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ISession, DeviceManagementSession, DeviceManagementPopulatedSession } from '@rocket.chat/core-typings'; -import type { PaginatedResult } from '@rocket.chat/rest-typings'; - -import type { SessionsPaginateProps } from './SessionsPaginateProps'; -import type { SessionsProps } from './SessionsProps'; - -declare module '@rocket.chat/rest-typings' { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Endpoints { - '/v1/sessions/list': { - GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array<DeviceManagementSession> }>; - }; - '/v1/sessions/info': { - GET: (params: SessionsProps) => DeviceManagementSession; - }; - '/v1/sessions/logout.me': { - POST: (params: SessionsProps) => Pick<ISession, 'sessionId'>; - }; - '/v1/sessions/list.all': { - GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array<DeviceManagementPopulatedSession> }>; - }; - '/v1/sessions/info.admin': { - GET: (params: SessionsProps) => DeviceManagementPopulatedSession; - }; - '/v1/sessions/logout': { - POST: (params: SessionsProps) => Pick<ISession, 'sessionId'>; - }; - } -} diff --git a/apps/meteor/ee/server/api/chat.ts b/apps/meteor/ee/server/api/chat.ts index faeca9455bb0..43eaa3cccf9c 100644 --- a/apps/meteor/ee/server/api/chat.ts +++ b/apps/meteor/ee/server/api/chat.ts @@ -1,8 +1,24 @@ import { Meteor } from 'meteor/meteor'; +import type { IMessage, ReadReceipt } from '@rocket.chat/core-typings'; import { API } from '../../../app/api/server/api'; import { hasLicense } from '../../app/license/server/license'; +type GetMessageReadReceiptsProps = { + messageId: IMessage['_id']; +}; + +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/chat.getMessageReadReceipts': { + GET: (params: GetMessageReadReceiptsProps) => { + receipts: ReadReceipt[]; + }; + }; + } +} + API.v1.addRoute( 'chat.getMessageReadReceipts', { authRequired: true }, diff --git a/apps/meteor/ee/server/api/engagementDashboard/channels.ts b/apps/meteor/ee/server/api/engagementDashboard/channels.ts index 8c2834253e06..b9d454ccf3e7 100644 --- a/apps/meteor/ee/server/api/engagementDashboard/channels.ts +++ b/apps/meteor/ee/server/api/engagementDashboard/channels.ts @@ -1,10 +1,37 @@ import { check, Match } from 'meteor/check'; +import type { IDirectMessageRoom, IRoom } from '@rocket.chat/core-typings'; import { API } from '../../../../app/api/server'; import { findAllChannelsWithNumberOfMessages } from '../../lib/engagementDashboard/channels'; import { isDateISOString, mapDateForAPI } from '../../lib/engagementDashboard/date'; import { getPaginationItems } from '../../../../app/api/server/helpers/getPaginationItems'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/engagement-dashboard/channels/list': { + GET: (params: { start: string; end: string; offset?: number; count?: number }) => { + channels: { + room: { + _id: IRoom['_id']; + name: IRoom['name'] | IRoom['fname']; + ts: IRoom['ts']; + t: IRoom['t']; + _updatedAt: IRoom['_updatedAt']; + usernames?: IDirectMessageRoom['usernames']; + }; + messages: number; + lastWeekMessages: number; + diffFromLastWeek: number; + }[]; + count: number; + offset: number; + total: number; + }; + }; + } +} + API.v1.addRoute( 'engagement-dashboard/channels/list', { diff --git a/apps/meteor/ee/server/api/engagementDashboard/messages.ts b/apps/meteor/ee/server/api/engagementDashboard/messages.ts index 2aac85c38b5c..826e03f40af1 100644 --- a/apps/meteor/ee/server/api/engagementDashboard/messages.ts +++ b/apps/meteor/ee/server/api/engagementDashboard/messages.ts @@ -1,4 +1,5 @@ import { check, Match } from 'meteor/check'; +import type { IDirectMessageRoom, IRoom } from '@rocket.chat/core-typings'; import { API } from '../../../../app/api/server'; import { @@ -8,6 +9,43 @@ import { } from '../../lib/engagementDashboard/messages'; import { isDateISOString, transformDatesForAPI } from '../../lib/engagementDashboard/date'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/engagement-dashboard/messages/origin': { + GET: (params: { start: string; end: string }) => { + origins: { + t: IRoom['t']; + messages: number; + }[]; + }; + }; + '/v1/engagement-dashboard/messages/top-five-popular-channels': { + GET: (params: { start: string; end: string }) => { + channels: { + t: IRoom['t']; + messages: number; + name: IRoom['name'] | IRoom['fname']; + usernames?: IDirectMessageRoom['usernames']; + }[]; + }; + }; + '/v1/engagement-dashboard/messages/messages-sent': { + GET: (params: { start: string; end: string }) => { + days: { day: Date; messages: number }[]; + period: { + count: number; + variation: number; + }; + yesterday: { + count: number; + variation: number; + }; + }; + }; + } +} + API.v1.addRoute( 'engagement-dashboard/messages/messages-sent', { diff --git a/apps/meteor/ee/server/api/engagementDashboard/users.ts b/apps/meteor/ee/server/api/engagementDashboard/users.ts index a5f921b24fa4..23b31a0d5236 100644 --- a/apps/meteor/ee/server/api/engagementDashboard/users.ts +++ b/apps/meteor/ee/server/api/engagementDashboard/users.ts @@ -1,4 +1,5 @@ import { check, Match } from 'meteor/check'; +import type { IUser } from '@rocket.chat/core-typings'; import { API } from '../../../../app/api/server'; import { @@ -10,6 +11,65 @@ import { } from '../../lib/engagementDashboard/users'; import { isDateISOString, transformDatesForAPI } from '../../lib/engagementDashboard/date'; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/engagement-dashboard/users/active-users': { + GET: (params: { start: string; end: string }) => { + month: { + day: number; + month: number; + year: number; + usersList: IUser['_id'][]; + users: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/chat-busier/weekly-data': { + GET: (params: { start: string }) => { + month: { + users: number; + day: number; + month: number; + year: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/chat-busier/hourly-data': { + GET: (params: { start: string }) => { + hours: { + users: number; + hour: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/users-by-time-of-the-day-in-a-week': { + GET: (params: { start: string; end: string }) => { + week: { + users: number; + hour: number; + day: number; + month: number; + year: number; + }[]; + }; + }; + '/v1/engagement-dashboard/users/new-users': { + GET: (params: { start: string; end: string }) => { + days: { day: Date; users: number }[]; + period: { + count: number; + variation: number; + }; + yesterday: { + count: number; + variation: number; + }; + }; + }; + } +} + API.v1.addRoute( 'engagement-dashboard/users/new-users', { diff --git a/apps/meteor/ee/server/api/roles.ts b/apps/meteor/ee/server/api/roles.ts index 5cad626fe99b..b3b666bd536a 100644 --- a/apps/meteor/ee/server/api/roles.ts +++ b/apps/meteor/ee/server/api/roles.ts @@ -1,13 +1,96 @@ import { Roles } from '@rocket.chat/models'; +import type { IRole } from '@rocket.chat/core-typings'; +import Ajv from 'ajv'; import { API } from '../../../app/api/server/api'; import { hasPermissionAsync } from '../../../app/authorization/server/functions/hasPermission'; import { settings } from '../../../app/settings/server/index'; import { isEnterprise } from '../../app/license/server'; -import { isRoleCreateProps, isRoleUpdateProps } from '../../definition/rest/v1/roles'; import { insertRoleAsync } from '../lib/roles/insertRole'; import { updateRole } from '../lib/roles/updateRole'; +const ajv = new Ajv({ + coerceTypes: true, +}); + +type RoleCreateProps = Pick<IRole, 'name'> & Partial<Pick<IRole, 'description' | 'scope' | 'mandatory2fa'>>; + +const roleCreatePropsSchema = { + type: 'object', + properties: { + name: { + type: 'string', + }, + description: { + type: 'string', + nullable: true, + }, + scope: { + type: 'string', + enum: ['Users', 'Subscriptions'], + nullable: true, + }, + mandatory2fa: { + type: 'boolean', + nullable: true, + }, + }, + required: ['name'], + additionalProperties: false, +}; + +export const isRoleCreateProps = ajv.compile<RoleCreateProps>(roleCreatePropsSchema); + +type RoleUpdateProps = { + roleId: IRole['_id']; + name: IRole['name']; +} & Partial<RoleCreateProps>; + +const roleUpdatePropsSchema = { + type: 'object', + properties: { + roleId: { + type: 'string', + }, + name: { + type: 'string', + }, + description: { + type: 'string', + nullable: true, + }, + scope: { + type: 'string', + enum: ['Users', 'Subscriptions'], + nullable: true, + }, + mandatory2fa: { + type: 'boolean', + nullable: true, + }, + }, + required: ['roleId', 'name'], + additionalProperties: false, +}; + +export const isRoleUpdateProps = ajv.compile<RoleUpdateProps>(roleUpdatePropsSchema); + +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/roles.create': { + POST: (params: RoleCreateProps) => { + role: IRole; + }; + }; + '/v1/roles.update': { + POST: (role: RoleUpdateProps) => { + role: IRole; + }; + }; + } +} + API.v1.addRoute( 'roles.create', { authRequired: true }, diff --git a/apps/meteor/ee/server/api/sessions.ts b/apps/meteor/ee/server/api/sessions.ts index 24ad74eca446..05106b79f037 100644 --- a/apps/meteor/ee/server/api/sessions.ts +++ b/apps/meteor/ee/server/api/sessions.ts @@ -1,19 +1,85 @@ import { Users, Sessions } from '@rocket.chat/models'; -import type { IUser } from '@rocket.chat/core-typings'; +import type { IUser, ISession, DeviceManagementSession, DeviceManagementPopulatedSession } from '@rocket.chat/core-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; +import type { PaginatedResult, PaginatedRequest } from '@rocket.chat/rest-typings'; +import Ajv from 'ajv'; -import { isSessionsPaginateProps, isSessionsProps } from '../../definition/rest/v1/sessions'; import { API } from '../../../app/api/server/api'; import { hasLicense } from '../../app/license/server/license'; import { Notifications } from '../../../app/notifications/server'; import { getPaginationItems } from '../../../app/api/server/helpers/getPaginationItems'; +const ajv = new Ajv({ coerceTypes: true }); + +type SessionsProps = { + sessionId: string; +}; + +const isSessionsProps = ajv.compile<SessionsProps>({ + type: 'object', + properties: { + sessionId: { + type: 'string', + }, + }, + required: ['sessionId'], + additionalProperties: false, +}); + +type SessionsPaginateProps = PaginatedRequest<{ + filter?: string; +}>; + +const isSessionsPaginateProps = ajv.compile<SessionsPaginateProps>({ + type: 'object', + properties: { + offset: { + type: 'number', + }, + count: { + type: 'number', + }, + filter: { + type: 'string', + }, + sort: { + type: 'string', + }, + }, + required: [], + additionalProperties: false, +}); + const validateSortKeys = (sortKeys: string[]): boolean => { const validSortKeys = ['loginAt', 'device.name', 'device.os.name', 'device.os.version', '_user.name', '_user.username']; return sortKeys.every((s) => validSortKeys.includes(s)); }; +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Endpoints { + '/v1/sessions/list': { + GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array<DeviceManagementSession> }>; + }; + '/v1/sessions/info': { + GET: (params: SessionsProps) => DeviceManagementSession; + }; + '/v1/sessions/logout.me': { + POST: (params: SessionsProps) => Pick<ISession, 'sessionId'>; + }; + '/v1/sessions/list.all': { + GET: (params: SessionsPaginateProps) => PaginatedResult<{ sessions: Array<DeviceManagementPopulatedSession> }>; + }; + '/v1/sessions/info.admin': { + GET: (params: SessionsProps) => DeviceManagementPopulatedSession; + }; + '/v1/sessions/logout': { + POST: (params: SessionsProps) => Pick<ISession, 'sessionId'>; + }; + } +} + API.v1.addRoute( 'sessions/list', { authRequired: true, validateParams: isSessionsPaginateProps }, From 8eaaafd0befeaeb146db39702727e70e5223d86a Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Tue, 18 Jul 2023 13:55:44 -0300 Subject: [PATCH 123/149] chore: Deprecate `cloud:checkRegisterStatus` method (#29853) --- apps/meteor/app/cloud/server/methods.ts | 4 +++ .../actions/hooks/useAdministrationItems.tsx | 5 ---- apps/meteor/client/startup/startup.ts | 4 ++- .../views/admin/cloud/RegisterWorkspace.tsx | 25 ++++++++----------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/apps/meteor/app/cloud/server/methods.ts b/apps/meteor/app/cloud/server/methods.ts index 797ed964c171..e6a6402fe560 100644 --- a/apps/meteor/app/cloud/server/methods.ts +++ b/apps/meteor/app/cloud/server/methods.ts @@ -40,6 +40,10 @@ declare module '@rocket.chat/ui-contexts' { } Meteor.methods<ServerMethods>({ + /** + * @deprecated this method is deprecated and will be removed soon. + * Prefer using cloud.registrationStatus rest api. + */ async 'cloud:checkRegisterStatus'() { const uid = Meteor.userId(); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx index ea678413a16b..531545b20a43 100644 --- a/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx +++ b/apps/meteor/client/sidebar/header/actions/hooks/useAdministrationItems.tsx @@ -65,11 +65,6 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => { const isAdmin = useRole('admin'); const setModal = useSetModal(); - // TODO: DEPRECATE IT - // const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); - // const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); - // const { workspaceRegistered } = result.data || {}; - const { data: registrationStatusData } = useRegistrationStatus(); const workspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; diff --git a/apps/meteor/client/startup/startup.ts b/apps/meteor/client/startup/startup.ts index 9295eacce245..440b55ce5e6d 100644 --- a/apps/meteor/client/startup/startup.ts +++ b/apps/meteor/client/startup/startup.ts @@ -71,7 +71,9 @@ Meteor.startup(() => { return; } - const { connectToCloud, workspaceRegistered } = await sdk.call('cloud:checkRegisterStatus'); + const { + registrationStatus: { connectToCloud, workspaceRegistered }, + } = await sdk.rest.get('/v1/cloud.registrationStatus'); c.stop(); if (connectToCloud === true && workspaceRegistered !== true) { diff --git a/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx b/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx index 9e4e479f16f9..694d437de8d3 100644 --- a/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx +++ b/apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx @@ -1,10 +1,9 @@ import { Box, Tag } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useSetModal, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; -import { useQuery } from '@tanstack/react-query'; +import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import Page from '../../../components/Page'; +import { useRegistrationStatus } from '../../../hooks/useRegistrationStatus'; import ManualWorkspaceRegistrationModal from './ManualWorkspaceRegistrationModal'; import RegisterWorkspaceCards from './components/RegisterWorkspaceCards'; import RegisterWorkspaceMenu from './components/RegisterWorkspaceMenu'; @@ -15,30 +14,28 @@ const RegisterWorkspace = () => { const t = useTranslation(); const setModal = useSetModal(); - const checkCloudRegisterStatus = useMethod('cloud:checkRegisterStatus'); - const result = useQuery(['admin/cloud/register-status'], async () => checkCloudRegisterStatus()); - const reload = useMutableCallback(() => result.refetch()); + const { data: registrationStatusData, isLoading, isError, refetch } = useRegistrationStatus(); + const isWorkspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false; + const isConnectedToCloud = registrationStatusData?.registrationStatus?.connectToCloud ?? false; - if (result.isLoading || result.isError) { + if (isLoading || isError) { return null; } - const { connectToCloud: isConnectedToCloud, workspaceRegistered: isWorkspaceRegistered } = result.data; - const handleRegisterWorkspaceClick = (): void => { const handleModalClose = (): void => { setModal(null); - reload(); + refetch(); }; if (isWorkspaceRegistered) { - setModal(<ConnectWorkspaceModal onClose={handleModalClose} onStatusChange={reload} />); - } else setModal(<RegisterWorkspaceModal onClose={handleModalClose} onStatusChange={reload} />); + setModal(<ConnectWorkspaceModal onClose={handleModalClose} onStatusChange={refetch} />); + } else setModal(<RegisterWorkspaceModal onClose={handleModalClose} onStatusChange={refetch} />); }; const handleManualWorkspaceRegistrationButton = (): void => { const handleModalClose = (): void => { setModal(null); - reload(); + refetch(); }; setModal(<ManualWorkspaceRegistrationModal onClose={handleModalClose} />); }; @@ -70,7 +67,7 @@ const RegisterWorkspace = () => { isWorkspaceRegistered={isWorkspaceRegistered} isConnectedToCloud={isConnectedToCloud} onClick={handleRegisterWorkspaceClick} - onStatusChange={reload} + onStatusChange={refetch} onClickOfflineRegistration={handleManualWorkspaceRegistrationButton} /> </Page.Header> From b31a880af8b164913108654736b0ce45f7f2f613 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Tue, 18 Jul 2023 15:48:03 -0300 Subject: [PATCH 124/149] test: Cover `sortMenu` hook (#29858) --- .../hooks/useGroupingListItems.spec.tsx | 25 +++++++++++++++ .../actions/hooks/useSortModeItems.spec.tsx | 19 ++++++++++++ .../actions/hooks/useViewModeItems.spec.tsx | 31 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx create mode 100644 apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx new file mode 100644 index 000000000000..b5779d825202 --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useGroupingListItems.spec.tsx @@ -0,0 +1,25 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useGroupingListItems } from './useGroupingListItems'; + +it('should render groupingList items', async () => { + const { result } = renderHook(() => useGroupingListItems()); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'unread', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'favorites', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'types', + }), + ); +}); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx new file mode 100644 index 000000000000..143d228fe7ca --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useSortModeItems.spec.tsx @@ -0,0 +1,19 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useSortModeItems } from './useSortModeItems'; + +it('should render sortMode items', async () => { + const { result } = renderHook(() => useSortModeItems()); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'activity', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'name', + }), + ); +}); diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx new file mode 100644 index 000000000000..6c6dd7532e7e --- /dev/null +++ b/apps/meteor/client/sidebar/header/actions/hooks/useViewModeItems.spec.tsx @@ -0,0 +1,31 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useViewModeItems } from './useViewModeItems'; + +it('should render viewMode items', async () => { + const { result } = renderHook(() => useViewModeItems()); + + expect(result.current[0]).toEqual( + expect.objectContaining({ + id: 'extended', + }), + ); + + expect(result.current[1]).toEqual( + expect.objectContaining({ + id: 'medium', + }), + ); + + expect(result.current[2]).toEqual( + expect.objectContaining({ + id: 'condensed', + }), + ); + + expect(result.current[3]).toEqual( + expect.objectContaining({ + id: 'avatars', + }), + ); +}); From 29b78ee655c4cb86bc9a295aca1bb0f957c78bba Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Tue, 18 Jul 2023 16:09:57 -0300 Subject: [PATCH 125/149] ci: Report Jest coverage (#29848) --- .github/workflows/ci-test-unit.yml | 5 + apps/meteor/.mocharc.client.js | 6 +- .../useToggleReactionMutation.spec.tsx | 106 ++++++++++++++++++ apps/meteor/jest.config.ts | 2 + codecov.yml | 10 ++ ee/packages/ddp-client/jest.config.ts | 1 + turbo.json | 2 +- 7 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx diff --git a/.github/workflows/ci-test-unit.yml b/.github/workflows/ci-test-unit.yml index f294ac2dda7b..03c6bc2352ab 100644 --- a/.github/workflows/ci-test-unit.yml +++ b/.github/workflows/ci-test-unit.yml @@ -31,3 +31,8 @@ jobs: - name: Unit Test run: yarn testunit + + - uses: codecov/codecov-action@v3 + with: + flags: unit + verbose: true diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 6e522ee67363..029564d7729a 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -38,5 +38,9 @@ module.exports = { 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts', ], - exclude: ['client/hooks/*.spec.{ts,tsx}', 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}'], + exclude: [ + 'client/hooks/*.spec.{ts,tsx}', + 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}', + 'client/components/message/content/reactions/**.spec.{ts,tsx}', + ], }; diff --git a/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx b/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx new file mode 100644 index 000000000000..1ef4d84ddc91 --- /dev/null +++ b/apps/meteor/client/components/message/content/reactions/useToggleReactionMutation.spec.tsx @@ -0,0 +1,106 @@ +import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext'; +import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext'; +import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext'; +import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; +import { renderHook, act } from '@testing-library/react-hooks'; +import React from 'react'; + +import { useToggleReactionMutation } from './useToggleReactionMutation'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // ✅ turns retries off + retry: false, + }, + }, + logger: { + log: (..._) => { + // Log debugging information + }, + warn: (..._) => { + // Log warning + }, + error: (..._) => { + // Log error + }, + }, +}); + +beforeEach(() => { + queryClient.clear(); +}); + +it('should be call rest `POST /v1/chat.react` method', async () => { + const fn = jest.fn(); + const { result, waitFor } = renderHook(() => useToggleReactionMutation(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={({ method, pathPattern, params }) => { + if (method === 'POST' && pathPattern === '/v1/chat.react') { + fn(params); + return { success: true } as any; + } + + throw new Error(`Endpoint not mocked: ${method} ${pathPattern}`); + }} + > + <MockedSettingsContext settings={{}}> + <MockedUserContext userPreferences={{}}> + <MockedAuthorizationContext permissions={[]}>{children}</MockedAuthorizationContext> + </MockedUserContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + act(async () => { + await result.current.mutateAsync({ mid: 'MID', reaction: 'smile' }); + }); + + await waitFor(() => result.current.isLoading === false); + + expect(fn).toHaveBeenCalledWith({ + messageId: 'MID', + reaction: 'smile', + }); +}); + +it('should not work for non-logged in users', async () => { + const fn = jest.fn(); + const { result, waitForValueToChange } = renderHook(() => useToggleReactionMutation(), { + wrapper: ({ children }) => ( + <QueryClientProvider client={queryClient}> + <MockedServerContext + handleRequest={({ method, pathPattern, params }) => { + if (method === 'POST' && pathPattern === '/v1/chat.react') { + fn(params); + return { success: true } as any; + } + + throw new Error(`Endpoint not mocked: ${method} ${pathPattern}`); + }} + > + <MockedSettingsContext settings={{}}> + <MockedAuthorizationContext permissions={[]}>{children}</MockedAuthorizationContext> + </MockedSettingsContext> + </MockedServerContext> + </QueryClientProvider> + ), + }); + + act(() => { + return result.current.mutate({ mid: 'MID', reaction: 'smile' }); + }); + + await waitForValueToChange(() => result.current.status); + + expect(fn).not.toHaveBeenCalled(); + + expect(result.current.status).toBe('error'); + + expect(result.current.error).toEqual(new Error('Not logged in')); +}); diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index bcd9f8f5432a..9232c2065dad 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -6,6 +6,7 @@ export default { testMatch: [ '<rootDir>/client/hooks/**.spec.[jt]s?(x)', '<rootDir>/client/components/**.spec.[jt]s?(x)', + '<rootDir>client/components/message/content/reactions/**.spec.[jt]s?(x)', '<rootDir>/client/sidebar/header/actions/hooks/**/**.spec.[jt]s?(x)', ], transform: { @@ -15,4 +16,5 @@ export default { '\\.css$': 'identity-obj-proxy', '^react($|/.+)': '<rootDir>/node_modules/react$1', }, + collectCoverage: true, }; diff --git a/codecov.yml b/codecov.yml index d9198cbf2443..0a3501678c1e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,5 +5,15 @@ coverage: default: target: auto threshold: 1% + client: + target: auto + flags: + - client +flags: + client: + paths: + - apps/meteor/client + carryforward: true + comment: layout: 'reach, diff, flags' diff --git a/ee/packages/ddp-client/jest.config.ts b/ee/packages/ddp-client/jest.config.ts index 0739147f43fe..eb3f38119a73 100644 --- a/ee/packages/ddp-client/jest.config.ts +++ b/ee/packages/ddp-client/jest.config.ts @@ -10,4 +10,5 @@ export default { moduleNameMapper: { '\\.css$': 'identity-obj-proxy', }, + collectCoverage: true, }; diff --git a/turbo.json b/turbo.json index e617f46d0bea..061b9fe02582 100644 --- a/turbo.json +++ b/turbo.json @@ -11,7 +11,7 @@ }, "testunit": { "dependsOn": ["build"], - "outputs": [] + "outputs": ["coverage/**"] }, "lint": { "dependsOn": ["build"], From dd2b98ee1c2ac70f825c41357f04d74a9bd697ac Mon Sep 17 00:00:00 2001 From: "lingohub[bot]" <69908207+lingohub[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:17:16 +0000 Subject: [PATCH 126/149] =?UTF-8?q?i18n:=20Language=20update=20from=20Ling?= =?UTF-8?q?oHub=20=F0=9F=A4=96=20on=202023-07-13Z=20(#29817)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 3 +-- apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json | 1 + apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json | 1 + 58 files changed, 58 insertions(+), 2 deletions(-) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json index cc915ea21186..858c2f4cf898 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/af.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR is slegs beskikbaar wanneer beide gebruikers aanlyn is", "Outgoing_WebHook": "Uitgaande WebHook", "Outgoing_WebHook_Description": "Kry data uit Rocket.Chat in real-time.", + "Outlook_Calendar_Enabled": "enabled", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Hersien URL aan watter lêers opgelaai word. Hierdie url word ook vir aflaai gebruik as 'n CDN nie gegee word nie", "Page_title": "Bladsy titel", "Page_URL": "Bladsy-URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json index f2691bb674b5..110c20b10792 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ar.i18n.json @@ -3287,6 +3287,7 @@ "Outgoing": "صادر", "Outgoing_WebHook": "خطاف الويب الصادر", "Outgoing_WebHook_Description": "الحصول على البيانات من Rocket.Chat في الوقت الفعلي.", + "Outlook_Calendar_Enabled": "تم التمكين", "Output_format": "تنسيق الإخراج", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "تجاوز عنوان URL الذي يتم تحميل الملفات إليه. يستخدم عنوان URL هذا أيضًا للتنزيلات ما لم يتم إعطاء CDN.", "Page_title": "عنوان الصفحة", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json index a44c25a1b95b..32f4af142ab4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/az.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR yalnız hər iki istifadəçi online olduğunda istifadə edilə bilər", "Outgoing_WebHook": "Giden WebHook", "Outgoing_WebHook_Description": "Real-time Rocket.Chat-dən məlumat əldə edin.", + "Outlook_Calendar_Enabled": "Etkin", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Dosyaların yükləndiyi URL'yi silin. Bu url də bir CDN verilmədiyi təqdirdə yükləmələr üçün də istifadə olunur", "Page_title": "Səhifə başlığı", "Page_URL": "Səhifə URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json index e448574f6b48..06c1ca5ce34d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/be-BY.i18n.json @@ -1893,6 +1893,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR даступны толькі тады, калі абодва карыстальніка знаходзяцца на сайце", "Outgoing_WebHook": "выходны WebHook", "Outgoing_WebHook_Description": "Атрымаць дадзеныя з Rocket.Chat ў рэжыме рэальнага часу.", + "Outlook_Calendar_Enabled": "Уключана", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL, да якога файлы будуць загружаныя. Гэтага URL выкарыстоўваецца таксама для загрузкі, калі толькі ў CDN даецца", "Page_title": "тытульны ліст", "Page_URL": "URL старонкі", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json index 35a907b728d3..7a50677816c4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bg.i18n.json @@ -1874,6 +1874,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR е налице само когато и двамата потребители са онлайн", "Outgoing_WebHook": "Изходяща WebHook", "Outgoing_WebHook_Description": "Извличайте данни от Rocket.Chat в реално време.", + "Outlook_Calendar_Enabled": "Позволено", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Замяна на URL адреса, към който се качват файловете. Този URL адрес също се използва за изтегляне, освен ако не е даден CDN", "Page_title": "Заглавие на страница", "Page_URL": "URL адрес на страницата", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json index 4776295db449..46d8f5f93d21 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/bs.i18n.json @@ -1870,6 +1870,7 @@ "OTR_is_only_available_when_both_users_are_online": "SP je dostupan samo ako su oba korisnika online", "Outgoing_WebHook": "Odlazni WebHook", "Outgoing_WebHook_Description": "Dobijte podatke iz Rocket.Chat u stvarnom vremenu.", + "Outlook_Calendar_Enabled": "Omogućeno", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL mjesta na koji su postavljene datoteke. Ovaj url se također koristi za preuzimanje, osim ako je zadan CDN", "Page_title": "Naslov stranice", "Page_URL": "URL stranice", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json index ddb2d9d3e259..b501f88a5000 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ca.i18n.json @@ -3248,6 +3248,7 @@ "Out_of_seats": "Sense llocs", "Outgoing_WebHook": "WebHook sortint", "Outgoing_WebHook_Description": "Extreu dades de Rocket.Chat en temps real.", + "Outlook_Calendar_Enabled": "Activat", "Output_format": "Format de sortida", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Reemplaça la URL a la qual es carreguen els fitxers. Aquest URL també es fa servir per a baixades a no ser que es proporcioni un CDN", "Page_title": "Titol de la pàgina", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json index 4d5b1232d9d2..435f09f3d185 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cs.i18n.json @@ -2723,6 +2723,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR je k dispozici pouze pokud jsou oba uživatelé online", "Outgoing_WebHook": "Odchozí Webhook", "Outgoing_WebHook_Description": "Získávejte data z Rocket.Chat v reálném čase.", + "Outlook_Calendar_Enabled": "Povoleno", "Output_format": "Výstupní formát", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Přepsat URL, na které jsou nahrané soubory. Tato adresa se také používá pro stahování pokud není nastavena CDN", "Page_title": "Titulku stránky", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json index 0c3bb7349d5d..eae2511cf07a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/cy.i18n.json @@ -1872,6 +1872,7 @@ "OTR_is_only_available_when_both_users_are_online": "Dim ond pan fydd y ddau ddefnyddiwr ar-lein mae OTR ar gael", "Outgoing_WebHook": "WebHook Allanol", "Outgoing_WebHook_Description": "Cael data allan o Rocket.Chat mewn amser real.", + "Outlook_Calendar_Enabled": "Wedi'i alluogi", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Anwybyddu'r URL y mae ffeiliau wedi'u llwytho i fyny. Defnyddiwyd yr url hwn hefyd i'w lawrlwytho oni bai bod CDN yn cael ei roi", "Page_title": "Teitl y dudalen", "Page_URL": "Tudalen URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json index e45395e5ab8c..065fd1a6527d 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/da.i18n.json @@ -2736,6 +2736,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR er kun tilgængelig, når begge brugere er online", "Outgoing_WebHook": "Udgående WebHook", "Outgoing_WebHook_Description": "Få data ud af Rocket.Chat i realtid.", + "Outlook_Calendar_Enabled": "Aktiveret", "Output_format": "Outputformat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Overstyr URL til hvilke filer der uploades. Denne url bruges også til download, medmindre en CDN er givet", "Page_title": "Sidetitel", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json index 465367b67ae8..fa2d8612b1f4 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de-AT.i18n.json @@ -1880,6 +1880,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ist nur möglich, wenn beide Benutzer online sind.", "Outgoing_WebHook": "Ausgehender WebHook", "Outgoing_WebHook_Description": "Holen Sie Daten in Echtzeit aus Rocket.Chat.", + "Outlook_Calendar_Enabled": "aktiviert", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL, wo die Dateien hochgeladen werden. Die URL wird auch für Downloads verwendet, wenn keine CDN angegeben wird.", "Page_title": "Seitentitel", "Page_URL": "Seiten-URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json index 9645db821afc..65c79f66f5af 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/de.i18n.json @@ -3700,6 +3700,7 @@ "Outgoing": "Ausgehend", "Outgoing_WebHook": "Ausgehender Webhook", "Outgoing_WebHook_Description": "Daten aus Rocket.Chat heraus versenden.", + "Outlook_Calendar_Enabled": "aktiviert", "Output_format": "Ausgabeformat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL, unter der die Dateien hochgeladen werden. Die URL wird auch für Downloads verwendet, wenn kein CDN angegeben wird", "Owner": "Eigentümer", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json index 74c40707b40d..f81634b9ce3f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/el.i18n.json @@ -1885,6 +1885,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR είναι διαθέσιμη μόνο όταν και οι δύο χρήστες είναι online", "Outgoing_WebHook": "Έξοδος WebHook", "Outgoing_WebHook_Description": "Αποκτήστε δεδομένα από το Rocket.Chat σε πραγματικό χρόνο.", + "Outlook_Calendar_Enabled": "Ενεργοποιήθηκε", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Παράκαμψη URL στο οποίο φορτώνονται τα αρχεία. Αυτή η διεύθυνση URL που χρησιμοποιείται επίσης για λήψεις εκτός εάν ένα CDN δίνεται", "Page_title": "Τίτλος σελίδας", "Page_URL": "Διεύθυνση Ιστοσελίδας", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 154bf6af2d6e..0140332a73b5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -3849,7 +3849,6 @@ "Outgoing_WebHook_Description": "Get data out of Rocket.Chat in real-time.", "Outlook_authentication": "Outlook authentication", "Outlook_authentication_disabled": "Outlook authentication disabled", - "Outlook_authentication_description": "Disable this to clear any outlook credentials stored in this machine.", "Outlook_calendar": "Outlook calendar", "Outlook_calendar_event": "Outlook calendar event", @@ -5951,4 +5950,4 @@ "Uninstall_grandfathered_app": "Uninstall {{appName}}?", "App_will_lose_grandfathered_status": "**This {{context}} app will lose its grandfathered status.** \n \nWorkspaces on Community Edition can have up to {{limit}} {{context}} apps enabled. Grandfathered apps count towards the limit but the limit is not applied to them.", "Theme_Appearence": "Theme Appearence" -} +} \ No newline at end of file diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json index 8006cd075d7a..e649f7f35387 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eo.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR estas nur havebla kiam ambaŭ uzantoj estas interrete", "Outgoing_WebHook": "Eliranta WebHook", "Outgoing_WebHook_Description": "Akiri datumojn el Rocket.Chat en reala tempo.", + "Outlook_Calendar_Enabled": "Enabled", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Anstataŭigi URL al kiu dosieroj estas alŝutitaj. Ĉi tiu url ankaŭ uzis por malŝarĝoj krom se ĝi ricevas CDN", "Page_title": "Paĝo-titolo", "Page_URL": "Paĝo URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json index 7e2219b43fb2..739768a79124 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/es.i18n.json @@ -3262,6 +3262,7 @@ "Outgoing": "Saliente", "Outgoing_WebHook": "Webhook saliente", "Outgoing_WebHook_Description": "Obtener datos de Rocket.Chat en tiempo real.", + "Outlook_Calendar_Enabled": "Habilitado", "Output_format": "Formato de salida", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Reemplazar la URL a la que se suben los archivos. Esta URL también se usa para descargas a menos que se proporcione una CDN", "Page_title": "Título de página", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json index 8b1e7125e15f..7585a29458f2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/eu.i18n.json @@ -96,6 +96,7 @@ "No_channels_yet": "Oraindik ez zara inongo kanalen partaide", "No_discussions_yet": "Ez dago eztabaidarik", "Options": "Aukerak", + "Outlook_Calendar_Enabled": "Gaituta", "Please_fill_name_and_email": "Sartu izena eta posta elektronikoa mesedez", "Private_Groups": "Talde Pribatuak", "Private_Groups_list": "Talde Pribatuen Zerrenda", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json index c23985c99cbc..a66a48d1b61b 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fa.i18n.json @@ -2181,6 +2181,7 @@ "OTR_is_only_available_when_both_users_are_online": "تنها زمانی در دسترس است که دو طرف آنلاین باشند.", "Outgoing_WebHook": "خروجی WebHook", "Outgoing_WebHook_Description": "دریافت اطلاعات از Rocket.Chat در زمان واقعی.", + "Outlook_Calendar_Enabled": "فعال", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL نادیده گرفتن که فایل های آپلود شده است. این URL نیز برای دریافت مگر اینکه یک CDN استفاده شده است", "Page_title": "عنوان صفحه", "Page_URL": "آدرس صفحه", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json index f3deb4a3063e..087fa6a0b27a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fi.i18n.json @@ -3754,6 +3754,7 @@ "Outgoing": "Lähtevät", "Outgoing_WebHook": "Lähtevä WebHook", "Outgoing_WebHook_Description": "Hanki tiedot chatsovelluksesta reaaliaikaisesti.", + "Outlook_Calendar_Enabled": "Käytössä", "Output_format": "Tulostusmuoto", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Ohitus-URL, johon tiedostot ladataan. Tätä URL-osoitetta käytetään myös vastakkaisen suuntaiseen lataamiseen, ellei CDN:ää ole annettu", "Owner": "Omistaja", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json index 5d956f706e85..776c3cfd7ae6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/fr.i18n.json @@ -3280,6 +3280,7 @@ "Outgoing": "Sortant", "Outgoing_WebHook": "Webhook sortant", "Outgoing_WebHook_Description": "Obtenez des données de Rocket.Chat en temps réel.", + "Outlook_Calendar_Enabled": "Activé", "Output_format": "Format de sortie", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Modifier l'URL vers laquelle les fichiers sont chargés. Cette URL est également utilisée pour les téléchargements sauf si un CDN est indiqué", "Page_title": "Titre de la page", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json index 09dc9a004067..6952be4a7824 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/gl.i18n.json @@ -231,6 +231,7 @@ "Online": "En liña", "Only_you_can_see_this_message": "So ti podes ver esta mensaxe", "Options": "Opcións", + "Outlook_Calendar_Enabled": "Activado", "Phone": "Teléfono", "Pin_Message": "Fixar mensaxe", "pin-message": "Fixar mensaxe", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json index ee0c52ca4a39..80a8a97845cf 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/he.i18n.json @@ -1021,6 +1021,7 @@ "others": "אחרים", "OTR": "OTR", "OTR_is_only_available_when_both_users_are_online": "OTR זמינה רק כאשר המשתמשים הוא במצב מקוון", + "Outlook_Calendar_Enabled": "מופעל", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "כתובת אתר דרוס שאליו קבצים מועלים. url זה משמש גם עבור הורדות אלא אם CDN ניתן", "Password": "ססמה", "Password_Change_Disabled": "מנהל המערכת שלך ביטל את האפשרות לשנות סיסמאות", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json index 0e74af00275e..33e3f43083f5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hi-IN.i18n.json @@ -191,6 +191,7 @@ "New_messages": "नए संदेश", "No": "नहीं", "Options": "विकल्प", + "Outlook_Calendar_Enabled": "सक्रिय", "Please_answer_survey": "कृपया इस चैट के बारे में त्वरित सर्वेक्षण का उत्तर देने के लिए एक क्षण लें", "Please_fill_name_and_email": "कृपया नाम और ईमेल भरें", "Public": "जनता", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json index 0db25d33568a..4a09d901d5df 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hr.i18n.json @@ -2010,6 +2010,7 @@ "OTR_is_only_available_when_both_users_are_online": "SP je dostupan samo ako su oba korisnika online", "Outgoing_WebHook": "Odlazni WebHook", "Outgoing_WebHook_Description": "Dobijte podatke iz Rocket.Chat u stvarnom vremenu.", + "Outlook_Calendar_Enabled": "Omogućeno", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL mjesta na koji su postavljene datoteke. Ovaj url se također koristi za preuzimanje, osim ako je zadan CDN", "Page_title": "Naslov stranice", "Page_URL": "URL stranice", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json index 6cbd626f7526..d3ef06c2355a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/hu.i18n.json @@ -3616,6 +3616,7 @@ "Outgoing": "Kimenő", "Outgoing_WebHook": "Kimenő webhorog", "Outgoing_WebHook_Description": "Adatok lekérése a Rocket.Chatből valós időben.", + "Outlook_Calendar_Enabled": "Engedélyezve", "Output_format": "Kimeneti formátum", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Az URL felülbírálása, ahová a fájlok feltöltésre kerülnek. Ez az URL letöltésekhez is használva van, kivéve ha tartalomkézbesítési hálózat van megadva.", "Owner": "Tulajdonos", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json index 8a42c8f07318..636095f71f85 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/id.i18n.json @@ -1885,6 +1885,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR hanya tersedia jika kedua pengguna sedang online", "Outgoing_WebHook": "WebHook keluar", "Outgoing_WebHook_Description": "Dapatkan data dari Rocket.Chat secara real-time.", + "Outlook_Calendar_Enabled": "Diaktifkan", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL Override yang file-upload. url ini juga digunakan untuk download kecuali CDN diberikan", "Page_title": "Judul halaman", "Page_URL": "Halaman URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json index decd72db4baa..e4745f79926a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/it.i18n.json @@ -1946,6 +1946,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR è disponibile solo se entrambi gli utenti sono on-line", "Outgoing_WebHook": "WebHook in uscita", "Outgoing_WebHook_Description": "Ottieni i dati da Rocket.Chat in tempo reale.", + "Outlook_Calendar_Enabled": "Abilitato", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL sostitutiva in cui vengono caricati i file. Questo URL è utilizzato anche per i download a meno che una CDN sia impostata", "Page_title": "Titolo pagina", "Page_URL": "URL pagina", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json index 18bd26b34c07..e5be1cb91ce5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ja.i18n.json @@ -3254,6 +3254,7 @@ "Outgoing": "発信中", "Outgoing_WebHook": "発信Webhook", "Outgoing_WebHook_Description": "Rocket.Chatからリアルタイムでデータを取得します。", + "Outlook_Calendar_Enabled": "有効", "Output_format": "出力形式", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "アップロードされたファイルのURLを上書きします。このURLは、CDNからURLが提供されない場合にダウンロードでも使用されます", "Page_title": "ページタイトル", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json index e95360251310..05d519a1f6c2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ka-GE.i18n.json @@ -2562,6 +2562,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ხელმისაწვდომია მხოლოდ როდესაც ორივე მომხმარებელი ონლაინ არის", "Outgoing_WebHook": "გამავალი WebHook", "Outgoing_WebHook_Description": "მიიღეთ მონაცემები Rocket.Chat– დან რეალურ დროში.", + "Outlook_Calendar_Enabled": "ჩართულია", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "შეცვალეთ ლინკი რომელზეც ფაილები აიტვირთება. ეს ლინკი ასევე გამოიყენება გადმოწერებისთვის თუ სხვა CDN არ არის მოწოდებული", "Page_title": "გვერდის სათაური", "Page_URL": "გვერდის URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json index 43f5ee0e1cd4..0dbd1d7c092c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/km.i18n.json @@ -2187,6 +2187,7 @@ "OTR_is_only_available_when_both_users_are_online": "ប្រវត្តិគឺអាចប្រើបានតែនៅពេលដែលអ្នកប្រើប្រាស់ទាំងពីរគឺមាននៅលើបណ្ដាញ", "Outgoing_WebHook": "WebHook ចេញ", "Outgoing_WebHook_Description": "ទទួលបានទិន្នន័យចេញពី Rocket.Chat ក្នុងពេលវេលាពិតប្រាកដ។", + "Outlook_Calendar_Enabled": "បានបើក", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL ដែលបានបដិសេធទៅនឹងឯកសារដែលបានផ្ទុកឡើង។ URL នេះបានប្រើផងដែរសម្រាប់ការទាញយកបានទេលុះត្រាតែ CDN មួយត្រូវបានផ្ដល់", "Page_title": "ចំណងជើងទំព័រ", "Page_URL": "URL ទំព័រ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json index f660099b04c7..2efde93240dd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ko.i18n.json @@ -2781,6 +2781,7 @@ "OTR_is_only_available_when_both_users_are_online": "두 사용자가 온라인 상태일 때만 비밀 대화를 사용할 수 있습니다.", "Outgoing_WebHook": "나가는 WebHook", "Outgoing_WebHook_Description": "Rocket.Chat에서 실시간으로 데이터를 가져옵니다.", + "Outlook_Calendar_Enabled": "사용", "Output_format": "출력 형식", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "파일이 업로드되는 재정의 URL. CDN이 지정되지 않으면, 다운로드 URL로 설정됩니다.", "Page_title": "페이지 제목", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json index da57c23dec1c..a48e1bab4f1f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ku.i18n.json @@ -1872,6 +1872,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR bi tenê dikarî de derbasdar e ku her du users are bike", "Outgoing_WebHook": "Outgoing WebHook", "Outgoing_WebHook_Description": "Agahdariya ji Rocket-ê dihêle.", + "Outlook_Calendar_Enabled": "çalake", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL Redkirin ji bo ku wêneyên barkirî bi. Ev url jî ji bo downloads eger CDN bikaranîn dayîn", "Page_title": "Title title", "Page_URL": "URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json index 25ea4a1dd26f..7e332c00d551 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lo.i18n.json @@ -1914,6 +1914,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ແມ່ນມີພຽງແຕ່ໃນເວລາທີ່ຜູ້ໃຊ້ທັງສອງອອນໄລນ໌", "Outgoing_WebHook": "Outgoing WebHook", "Outgoing_WebHook_Description": "ໄດ້ຮັບຂໍ້ມູນອອກຈາກ RocketChat ໃນເວລາທີ່ແທ້ຈິງ.", + "Outlook_Calendar_Enabled": "ເປີດການໃຊ້ງານ", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL ແທນທີ່ໄຟລ໌ໄດ້ຖືກອັບໂຫລດ. url ນີ້ຍັງສາມາດໃຊ້ສໍາລັບການດາວໂຫລດເວັ້ນເສຍແຕ່ວ່າແຄນາດາຈະໄດ້ຮັບ", "Page_title": "Page title", "Page_URL": "ຫນ້າ URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json index 43d780cd153d..ba328eb2a2f5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lt.i18n.json @@ -1932,6 +1932,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR galima tik tada, kai abu vartotojai yra prisijungę", "Outgoing_WebHook": "Išeinantis WebHook", "Outgoing_WebHook_Description": "Gauti duomenis iš \"Rocket.Chat\" realiuoju laiku.", + "Outlook_Calendar_Enabled": "Įjungtas", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Nepaisyti URL, į kurį įkelti failai. Šis URL taip pat naudojamas atsisiuntimui, jei nėra CDN", "Page_title": "Puslapio pavadinimas", "Page_URL": "Puslapio URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json index b8e0f5d84a66..9b5187f13b17 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/lv.i18n.json @@ -1890,6 +1890,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR ir pieejams tikai tad, ja abi lietotāji ir tiešsaistē", "Outgoing_WebHook": "Izejošais WebHook", "Outgoing_WebHook_Description": "Reāllaikā iegūt datus no Rocket.Chat.", + "Outlook_Calendar_Enabled": "Iespējots", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Pārrakstīt URL, uz kuru faili tika augšupielādēti. Šis URL tiek izmantots arī lejupielādei, ja nav norādīts CDN", "Page_title": "Lapas nosaukums", "Page_URL": "Lapas URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json index a78f07857973..1900ae9e2554 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/mn.i18n.json @@ -1872,6 +1872,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR нь хоёулаа онлайн байх үед л боломжтой", "Outgoing_WebHook": "Гарах WebHook", "Outgoing_WebHook_Description": "Рокет.Chat-ээс өгөгдлийг бодит цагт авах.", + "Outlook_Calendar_Enabled": "Идэвхжүүлсэн", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Файлуудыг байршуулсан URL-ыг хэтрүүлэх. Энэ url нь CDN өгөгдөөгүй бол татан авахад хэрэглэгддэг", "Page_title": "Хуудасны гарчиг", "Page_URL": "Хуудасны URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json index 0749c30b656f..9c3437af7dda 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ms-MY.i18n.json @@ -1884,6 +1884,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR hanya tersedia apabila kedua-dua pengguna sedang online", "Outgoing_WebHook": "WebHook keluar", "Outgoing_WebHook_Description": "Dapatkan data dari Rocket.Chat secara real-time.", + "Outlook_Calendar_Enabled": "didayakan", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL Override untuk fail yang dimuat naik. url ini juga digunakan untuk muat turun melainkan CDN diberikan", "Page_title": "Tajuk halaman", "Page_URL": "URL halaman", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json index fdc52656a9c8..b132f58ad1c6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/nl.i18n.json @@ -3273,6 +3273,7 @@ "Outgoing": "Uitgaande", "Outgoing_WebHook": "Uitgaande WebHook", "Outgoing_WebHook_Description": "Haal uit Rocket.Chat in realtime.", + "Outlook_Calendar_Enabled": "Ingeschakeld", "Output_format": "Uitvoerformaat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Overschrijf de URL waarnaar bestanden worden geüpload. Deze url wordt ook gebruikt voor downloads, tenzij een CDN is opgegeven", "Page_title": "Pagina titel", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json index e3ce45d342d8..5f078e63f3b6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/no.i18n.json @@ -1964,6 +1964,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR er bare tilgjengelig når begge brukerne er online", "Outgoing_WebHook": "Utgående WebHook", "Outgoing_WebHook_Description": "Få data ut av Rocket.Chat i sanntid.", + "Outlook_Calendar_Enabled": "aktivert", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Overstyr URL-adressen til hvilke filer som lastes opp. Denne nettadressen brukes også til nedlastinger med mindre en CDN er gitt", "Page_title": "Side tittel", "Page_URL": "Side URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json index ee9d3c9a95f3..4d97d2559461 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pl.i18n.json @@ -3554,6 +3554,7 @@ "Outgoing": "Wychodzący", "Outgoing_WebHook": "Wychodzący WebHook", "Outgoing_WebHook_Description": "Uzyskaj dane z Rocket.Chat w czasie rzeczywistym.", + "Outlook_Calendar_Enabled": "Włączone", "Output_format": "Format wyjściowy", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Zastąp adres URL, do którego są przesyłane pliki. Ten url również wykorzystywane do pobrania, chyba że podano CDN", "Owner": "Właściciel", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index efbe4b426654..7d85f1ae67e7 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -3310,6 +3310,7 @@ "Outgoing": "Enviado", "Outgoing_WebHook": "WebHook de saída", "Outgoing_WebHook_Description": "Obtenha dados de Rocket.Chat em tempo real.", + "Outlook_Calendar_Enabled": "Habilitado", "Output_format": "Formato de saída", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Substituir URL para os arquivos que foram carregados. Este URL também será usado para downloads, a menos que um CDN seja fornecido", "Page_title": "Título da página", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json index 3e2ea1db9c0f..e1e827e21cdd 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -2189,6 +2189,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR só está disponível quando os utilizadores estão online", "Outgoing_WebHook": "WebHook de saída", "Outgoing_WebHook_Description": "Obtenha os dados de Rocket.Chat em tempo real.", + "Outlook_Calendar_Enabled": "Habilitado", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Substituir URL para a qual os arquivos são carregados. Esta url também será usada para downloads, a menos que um CDN seja fornecido", "Page_title": "Título da página", "Page_URL": "URL da página", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json index 4a9bf52c9456..8066a2a0bac5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ro.i18n.json @@ -1876,6 +1876,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR este disponibilă numai atunci când ambii utilizatori sunt online", "Outgoing_WebHook": "WebHook de ieșire", "Outgoing_WebHook_Description": "Obțineți date din Rocket.Chat în timp real.", + "Outlook_Calendar_Enabled": "Activat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "URL-ul supracontrol la care sunt încărcate fișiere. Această adresă URL, de asemenea, utilizat pentru download-uri decât dacă o CDN este dată", "Page_title": "Titlul paginii", "Page_URL": "Adresa URL a paginii", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json index 42f750b956db..31c421a22be2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -3433,6 +3433,7 @@ "Outgoing": "Исходящие", "Outgoing_WebHook": "Исходящий WebHook", "Outgoing_WebHook_Description": "Получать данные из Rocket.Chat в режиме реального времени", + "Outlook_Calendar_Enabled": "Включено", "Output_format": "Формат вывода", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Переопределить URL-адрес, на который загружены файлы. Этот URL-адрес также используется для загрузок до тех пор, пока не указан CDN.", "Page_title": "Заголовок страницы", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json index eb54defefe86..7504f0812efa 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sk-SK.i18n.json @@ -1886,6 +1886,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR je k dispozícii iba vtedy, ak sú obaja používatelia online", "Outgoing_WebHook": "Odchádzajúci WebHook", "Outgoing_WebHook_Description": "Získajte dáta z Rocket.Chat v reálnom čase.", + "Outlook_Calendar_Enabled": "Povolené", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Prepísať adresu URL, do ktorej sú nahrané súbory. Táto adresa URL sa tiež používa na sťahovanie, ak nie je zadaná žiadosť o CDN", "Page_title": "Názov stránky", "Page_URL": "Adresa URL stránky", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json index 954c54830717..89e48c8937a5 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sl-SI.i18n.json @@ -1866,6 +1866,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR na voljo samo, ko sta oba uporabnika dosegljiva", "Outgoing_WebHook": "Odhodni WebHook", "Outgoing_WebHook_Description": "Pridobite podatke iz aplikacije Rocket.Chat v realnem času.", + "Outlook_Calendar_Enabled": "Omogočeno", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Prevozi URL, na katerega so naložene datoteke. Ta URL je bil uporabljen tudi za prenose, razen če je podan CDN", "Page_title": "Naslov strani", "Page_URL": "URL strani", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json index fdd705dd663b..a98a96b6a10c 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sq.i18n.json @@ -1876,6 +1876,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR është në dispozicion vetëm kur të dy përdoruesit janë në internet", "Outgoing_WebHook": "WebHook që po largohet", "Outgoing_WebHook_Description": "Merrni të dhëna nga Rocket.Chat në kohë reale.", + "Outlook_Calendar_Enabled": "enabled", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL të cilat fotografi janë ngarkuar. Kjo url përdorur edhe për shkarkime, përveç nëse një CDN është dhënë", "Page_title": "Titulli i faqes", "Page_URL": "URL e faqes", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json index 725a094d4e2c..21eb34b49b3a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sr.i18n.json @@ -1708,6 +1708,7 @@ "OTR": "ОТР", "Outgoing_WebHook": "Одлазни ВебХоок", "Outgoing_WebHook_Description": "Добијте податке из Роцкет.Цхат у реалном времену.", + "Outlook_Calendar_Enabled": "Омогућено", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Замени УРЛ адреса за датотеке које су уплоадед. Овај УРЛ се користи за преузимање, осим ако ЦДН дат", "Page_title": "Наслов странице", "Page_URL": "УРЛ адреса странице", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json index 873952ba420c..584725906cd2 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/sv.i18n.json @@ -3759,6 +3759,7 @@ "Outgoing": "Utgående", "Outgoing_WebHook": "Utgående WebHook", "Outgoing_WebHook_Description": "Hämta data från Rocket.Chat i realtid.", + "Outlook_Calendar_Enabled": "Aktiverad", "Output_format": "Utdataformat", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Åsidosätt URL till vilken filer som laddas upp. Denna url används också för nedladdning såvida inte en CDN anges", "Owner": "Ägare", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json index fb9d1cffe1af..02682e3aeea0 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ta-IN.i18n.json @@ -1877,6 +1877,7 @@ "OTR_is_only_available_when_both_users_are_online": "இரண்டு பயனர்கள் ஆன்லைனில் இருக்கும் போது OTR மட்டுமே உள்ளது", "Outgoing_WebHook": "வெளிச்செல்லும் WebHook", "Outgoing_WebHook_Description": "நிகழ்நேரத்தில் ராக்கெட்.சட்டை வெளியே தரவு கிடைக்கும்.", + "Outlook_Calendar_Enabled": "இயக்கப்பட்டது", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "மீறு URL ஐ கோப்புகளை பதிவேற்றப்படும் வேண்டும். ஒரு வலம்புரி வரை இறக்கம் பயன்படுத்தப்படும் இந்த URL வழங்கப்படும்", "Page_title": "பக்க தலைப்பு", "Page_URL": "பக்க URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json index 7574fb516b5f..98c7f58e3130 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/th-TH.i18n.json @@ -1870,6 +1870,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR จะใช้ได้เฉพาะเมื่อผู้ใช้ทั้งสองออนไลน์เท่านั้น", "Outgoing_WebHook": "WebHook ขาออก", "Outgoing_WebHook_Description": "รับข้อมูลจาก Rocket.Chat แบบเรียลไทม์", + "Outlook_Calendar_Enabled": "เปิดใช้งานแล้ว", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "แทนที่ URL ที่อัปโหลดไฟล์ URL นี้ใช้สำหรับการดาวน์โหลดเว้นแต่จะได้รับ CDN", "Page_title": "ชื่อหน้า", "Page_URL": "URL ของหน้าเว็บ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json index 4b5a57995436..5c78d413e7f6 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/tr.i18n.json @@ -2245,6 +2245,7 @@ "OTR_is_only_available_when_both_users_are_online": "Kayıt Dışı, yalnızca her iki kullanıcı da çevrimiçi ise kullanılabilir.", "Outgoing_WebHook": "Giden WebHook", "Outgoing_WebHook_Description": "Gerçek zamanlı olarak Rocket.Chat'ten veri alın.", + "Outlook_Calendar_Enabled": "Etkin", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "dosyaların yüklendiği hangi geçersiz kıl URL. Ayrıca CDN sürece indirme için kullanılan bu url verilir", "Page_title": "Sayfa başlığı", "Page_URL": "Sayfa URL'si", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json index e72486a80a6f..a007147fb810 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/ug.i18n.json @@ -792,6 +792,7 @@ "others": "باشقا", "OTR": "خاتىرىلەنمەيدىغان دىيالوگ", "OTR_is_only_available_when_both_users_are_online": "خاتىرىلەنمەيدىغان دىيالوگ پەقەت ئىككى تەرەپ توردا بولغاندا ئاندىن ئىشلەتكىلى بولىدۇ", + "Outlook_Calendar_Enabled": "ئىشلىتىشكە باشلىدى", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "مۇ چۈشۈرۈش ئۇلانمىسى بولۇپ ئىشلىتىلىدۇ .urlنى تەڭشىمىگەن بولسىڭىز ، CDNنى قايتا يېزىپ ئۇنى ھۆججەت چىقىرىش ئادرېسى قىلىپ بېكىتىڭ. ئەگەر URL", "Password": "پارول", "Password_Change_Disabled": "باشقۇرغۇچىڭىز ئاللىبۇرۇن پارولنى ئۆزگەرتىش ئىقتىدارىنى چەكلىدىRocket.Chatسىزنىڭ", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json index 9919c95fc36e..c7a1d9f5e576 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/uk.i18n.json @@ -2406,6 +2406,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR доступний тільки тоді, коли обидва користувачі знаходяться в мережі", "Outgoing_WebHook": "Вихідний WebHook", "Outgoing_WebHook_Description": "Отримайте дані поза Rocket.Chat в режимі реального часу.", + "Outlook_Calendar_Enabled": "Включено", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Override URL, до якого файли завантажуються. Цей URL-адресу також використовується для завантаження, якщо тільки в CDN дається", "Page_title": "Назва сторінки", "Page_URL": "URL-адреса сторінки", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json index d531bd034f62..d2f19f8d73ce 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/vi-VN.i18n.json @@ -1977,6 +1977,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR chỉ khả dụng khi cả người dùng trực tuyến", "Outgoing_WebHook": "WebHook gửi đi", "Outgoing_WebHook_Description": "Lấy dữ liệu ra khỏi Rocket.Chat theo thời gian thực.", + "Outlook_Calendar_Enabled": "Đã bật", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "Ghi đè URL mà tệp được tải lên. Url này cũng được sử dụng cho việc tải xuống trừ khi có CDN", "Page_title": "Tiêu đề trang", "Page_URL": "URL Trang", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json index ed085a4b9630..49ec4bee3d66 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-HK.i18n.json @@ -1901,6 +1901,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR仅在两个用户在线时才可用", "Outgoing_WebHook": "即将离任的WebHook", "Outgoing_WebHook_Description": "实时获取Rocket.Chat数据。", + "Outlook_Calendar_Enabled": "已啟用", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "覆盖文件上传到的URL。此网址也用于下载,除非提供CDN", "Page_title": "页面标题", "Page_URL": "页面URL", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json index a23e2152c5cf..6e9480b1bb5a 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh-TW.i18n.json @@ -3159,6 +3159,7 @@ "Outgoing": "傳出", "Outgoing_WebHook": "外部的 WebHook", "Outgoing_WebHook_Description": "即時獲取 Rocket.Chat 資料。", + "Outlook_Calendar_Enabled": "啟用", "Output_format": "輸出格式", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "哪些檔案被上傳覆蓋網址。該網址也可用於下載,除非CDN放出", "Page_title": "頁面標題", diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json index 17b890b82245..84b95cc7cf02 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/zh.i18n.json @@ -2843,6 +2843,7 @@ "OTR_is_only_available_when_both_users_are_online": "OTR 只能在双方均在线时使用", "Outgoing_WebHook": "出站 WebHook", "Outgoing_WebHook_Description": "实时获取Rocket.Chat数据。", + "Outlook_Calendar_Enabled": "已启用", "Output_format": "输出格式", "Override_URL_to_which_files_are_uploaded_This_url_also_used_for_downloads_unless_a_CDN_is_given": "重写 URL 为文件上传地址。如果没有设置 CDN,url 也会被当作下载链接。", "Page_title": "页面标题", From ffea6f4b74745dab85cbbf25ce26edb3f473f491 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Tue, 18 Jul 2023 23:09:46 -0300 Subject: [PATCH 127/149] regression: Remove wrong navigation bar feature preview image (#29859) --- packages/ui-client/src/hooks/useFeaturePreviewList.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ui-client/src/hooks/useFeaturePreviewList.ts b/packages/ui-client/src/hooks/useFeaturePreviewList.ts index 87a89e9d5a67..03d31c938e76 100644 --- a/packages/ui-client/src/hooks/useFeaturePreviewList.ts +++ b/packages/ui-client/src/hooks/useFeaturePreviewList.ts @@ -26,7 +26,6 @@ export const defaultFeaturesPreview: FeaturePreviewProps[] = [ i18n: 'Navigation_bar', description: 'Navigation_bar_description', group: 'Navigation', - imageUrl: 'images/featurePreview/quick-reactions.png', value: false, }, ]; From 287878c1eea0fd1140ca75a17dbd58e82eb7a10b Mon Sep 17 00:00:00 2001 From: Tasso Evangelista <tasso.evangelista@rocket.chat> Date: Tue, 18 Jul 2023 23:35:45 -0300 Subject: [PATCH 128/149] chore(lint): Reinforce ESLint for React (#29857) --- apps/meteor/.eslintrc.json | 25 +------- .../ComposerBoxPopupPreview.tsx | 2 +- apps/meteor/client/.eslintrc.json | 25 +------- .../DefaultParentRoomField.tsx | 2 +- .../InfoPanel/InfoPanel.stories.tsx | 2 +- .../Omnichannel/modals/ForwardChatModal.tsx | 30 +++++----- .../urlPreviews/OEmbedPreviewContent.tsx | 2 +- .../content/urlPreviews/UrlImagePreview.tsx | 2 +- .../client/sidebar/Item/Condensed.stories.tsx | 6 +- apps/meteor/client/sidebar/Item/Condensed.tsx | 6 +- .../client/sidebar/Item/Extended.stories.tsx | 6 +- apps/meteor/client/sidebar/Item/Extended.tsx | 6 +- .../client/sidebar/Item/Medium.stories.tsx | 6 +- apps/meteor/client/sidebar/Item/Medium.tsx | 6 +- .../FederatedRoomListItem.tsx | 2 +- .../MatrixFederationManageServerModal.tsx | 2 +- .../actions/OmnichannelLivechatToggle.tsx | 2 +- .../AccountTokensTable/AccountTokensTable.tsx | 10 ++-- .../RegisterWorkspaceSetupStepOneModal.tsx | 2 +- .../moderation/ModerationConsoleTable.tsx | 18 ++---- .../permissions/CustomRoleUpsellModal.tsx | 2 +- .../views/admin/permissions/RoleForm.tsx | 2 +- .../client/views/admin/rooms/RoomsTable.tsx | 19 ++---- .../client/views/admin/settings/Section.tsx | 2 +- .../groups/voip/VoipExtensionsPage.tsx | 2 +- .../tabs/users/UsersTable/UsersTable.tsx | 4 +- apps/meteor/client/views/meet/CallPage.tsx | 2 +- apps/meteor/client/views/meet/MeetPage.tsx | 2 +- .../omnichannel/agents/AgentInfoActions.tsx | 4 +- .../MessageList/ContactHistoryMessage.tsx | 4 +- .../departments/EditDepartment.tsx | 12 ++-- .../directory/ChatsContextualBar.tsx | 2 +- .../directory/ContactContextualBar.tsx | 2 +- .../omnichannel/directory/calls/CallTable.tsx | 6 +- .../Omnichannel/OmnichannelRoomHeader.tsx | 2 +- apps/meteor/client/views/room/RoomOpener.tsx | 6 +- .../ExportMessages/MailExportForm.tsx | 2 +- .../contextualBar/Info/RoomInfo/RoomInfo.tsx | 2 +- .../NotificationPreferencesForm.tsx | 2 +- .../views/setupWizard/steps/AdminInfoStep.tsx | 2 +- .../teams/contextualBar/info/TeamsInfo.tsx | 16 +++-- .../cannedResponses/CannedResponsesTable.tsx | 2 +- .../components/cannedResponseForm.tsx | 2 +- .../priorities/PriorityEditForm.tsx | 2 +- .../omnichannel/slaPolicies/SlaTable.tsx | 2 +- .../client/omnichannel/units/UnitsTable.tsx | 6 +- .../DeviceManagementAccountTable.tsx | 10 ++-- .../DeviceManagementAdminTable.tsx | 16 ++--- .../channels/ChannelsOverview.tsx | 2 +- .../messages/MessagesPerChannelSection.tsx | 2 +- apps/meteor/tests/e2e/.eslintrc.json | 4 +- ee/packages/pdf-worker/.eslintrc.json | 2 +- ee/packages/ui-theming/.eslintrc.json | 2 +- .../ui-theming/src/PaletteStyleTag.tsx | 3 +- .../ui-theming/src/SidebarPaletteStyleTag.tsx | 2 +- .../ui-theming/src/hooks/useThemeMode.ts | 13 ++++- package.json | 2 +- packages/eslint-config/react.js | 32 ++++++++++ packages/fuselage-ui-kit/.eslintrc.js | 7 ++- packages/gazzodown/.eslintrc.json | 1 + packages/mock-providers/.eslintrc.json | 2 +- packages/ui-client/.eslintrc.json | 23 ++------ packages/ui-composer/.eslintrc.json | 21 +------ packages/ui-contexts/.eslintrc.json | 2 +- packages/ui-video-conf/.eslintrc.json | 23 ++------ packages/web-ui-registration/.eslintrc.json | 2 +- .../components/LoginSwitchLanguageFooter.tsx | 2 +- yarn.lock | 58 +++++++++---------- 68 files changed, 238 insertions(+), 266 deletions(-) create mode 100644 packages/eslint-config/react.js diff --git a/apps/meteor/.eslintrc.json b/apps/meteor/.eslintrc.json index 31d63e993611..a338ba226b3b 100644 --- a/apps/meteor/.eslintrc.json +++ b/apps/meteor/.eslintrc.json @@ -1,5 +1,5 @@ { - "extends": ["@rocket.chat/eslint-config", "plugin:you-dont-need-lodash-underscore/compatible"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react", "plugin:you-dont-need-lodash-underscore/compatible"], "globals": { "__meteor_bootstrap__": false, "__meteor_runtime_config__": false, @@ -7,13 +7,7 @@ "chrome": false, "jscolor": false }, - "plugins": ["react", "react-hooks"], "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": [ "warn", { @@ -21,11 +15,6 @@ } ] }, - "settings": { - "react": { - "version": "detect" - } - }, "overrides": [ { "files": ["**/*.ts", "**/*.tsx"], @@ -33,12 +22,7 @@ "Atomics": "readonly", "SharedArrayBuffer": "readonly" }, - "plugins": ["react"], "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], "@typescript-eslint/no-misused-promises": [ "error", { @@ -53,12 +37,7 @@ "parserOptions": { "project": ["./tsconfig.json"] }, - "excludedFiles": [".scripts/*.ts"], - "settings": { - "react": { - "version": "detect" - } - } + "excludedFiles": [".scripts/*.ts"] }, { "files": ["**/*.tests.js", "**/*.tests.ts", "**/*.spec.ts"], diff --git a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx index 45188e62122c..89c7de840820 100644 --- a/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx +++ b/apps/meteor/app/ui-message/client/popup/components/composerBoxPopupPreview/ComposerBoxPopupPreview.tsx @@ -19,7 +19,7 @@ const ComposerBoxPopupPreview = forwardRef< tmid?: string; suspended?: boolean; } ->(({ focused, items, rid, tmid, select, suspended }, ref) => { +>(function ComposerBoxPopupPreview({ focused, items, rid, tmid, select, suspended }, ref) { const id = useUniqueId(); const chat = useChat(); const executeSlashCommandPreviewMethod = useMethod('executeSlashCommandPreview'); diff --git a/apps/meteor/client/.eslintrc.json b/apps/meteor/client/.eslintrc.json index 286fbb957017..f772d4366e0e 100644 --- a/apps/meteor/client/.eslintrc.json +++ b/apps/meteor/client/.eslintrc.json @@ -1,6 +1,6 @@ { "root": true, - "extends": ["@rocket.chat/eslint-config/original", "prettier", "plugin:you-dont-need-lodash-underscore/compatible"], + "extends": ["@rocket.chat/eslint-config/original", "@rocket.chat/eslint-config/react", "prettier", "plugin:you-dont-need-lodash-underscore/compatible"], "parser": "@babel/eslint-parser", "plugins": ["react", "react-hooks", "prettier", "testing-library", "anti-trojan-source"], "rules": { @@ -29,14 +29,6 @@ } ], "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/jsx-key": ["error", { "checkFragmentShorthand": true, "checkKeyMustBeforeSpread": true, "warnOnDuplicates": true }], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": [ "warn", { @@ -50,9 +42,6 @@ "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "env": { @@ -63,7 +52,6 @@ { "files": ["**/*.ts", "**/*.tsx"], "extends": ["@rocket.chat/eslint-config"], - "plugins": ["react", "react-hooks"], "rules": { "@typescript-eslint/naming-convention": [ "error", @@ -136,14 +124,6 @@ } ], "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/jsx-key": ["error", { "checkFragmentShorthand": true, "checkKeyMustBeforeSpread": true, "warnOnDuplicates": true }], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": [ "warn", { @@ -160,9 +140,6 @@ "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } } }, diff --git a/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx b/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx index 88de1d35051f..0a2717a65552 100644 --- a/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx +++ b/apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx @@ -23,7 +23,7 @@ const DefaultParentRoomField = ({ defaultParentRoom }: { defaultParentRoom: stri } if (!value || !value.room) { - return <Callout type={'danger'}>{t('Error')}</Callout>; + return <Callout type='danger'>{t('Error')}</Callout>; } return ( diff --git a/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx b/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx index 9d29007f9b90..4e8e44b1f932 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanel.stories.tsx @@ -24,7 +24,7 @@ export const Default: ComponentStory<typeof InfoPanel> = () => ( <InfoPanel> <InfoPanel.Avatar /> <InfoPanel.Section> - <InfoPanel.Title title='rocketchat-frontend-team' icon={'hashtag'} /> + <InfoPanel.Title title='rocketchat-frontend-team' icon='hashtag' /> </InfoPanel.Section> <InfoPanel.Section> diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx index 9ff01a4b7d6c..4754e0bcbd63 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx @@ -101,24 +101,22 @@ const ForwardChatModal = ({ <Modal.Close onClick={onCancel} /> </Modal.Header> <Modal.Content fontScale='p2'> - <Field mbe={'x30'}> + <Field mbe='x30'> <Field.Label>{t('Forward_to_department')}</Field.Label> <Field.Row> - { - <PaginatedSelectFiltered - withTitle - filter={departmentsFilter as string} - setFilter={setDepartmentsFilter} - options={departments} - maxWidth='100%' - placeholder={t('Select_an_option')} - onChange={(value: string): void => { - setValue('department', value); - }} - flexGrow={1} - endReached={endReached} - /> - } + <PaginatedSelectFiltered + withTitle + filter={departmentsFilter as string} + setFilter={setDepartmentsFilter} + options={departments} + maxWidth='100%' + placeholder={t('Select_an_option')} + onChange={(value: string): void => { + setValue('department', value); + }} + flexGrow={1} + endReached={endReached} + /> </Field.Row> </Field> <Divider children={t('or')} /> diff --git a/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx b/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx index 2955a1eb5b39..97a01c552523 100644 --- a/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx +++ b/apps/meteor/client/components/message/content/urlPreviews/OEmbedPreviewContent.tsx @@ -39,7 +39,7 @@ const OEmbedPreviewContent = ({ <MessageGenericPreviewFooter> <Box display='flex' justifyContent='flex-start'> {showSiteName && <MarkdownText variant='inline' content={`[${siteName}](${siteUrl})`} />} - {showFooterSeparator && <Box marginInline='x4'>{'|'}</Box>} + {showFooterSeparator && <Box marginInline='x4'>|</Box>} {showAuthorName && <MarkdownText variant='inline' content={`[${authorName}](${authorUrl})`} />} </Box> </MessageGenericPreviewFooter> diff --git a/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx b/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx index 1227faee6eeb..f56adef8f12d 100644 --- a/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx +++ b/apps/meteor/client/components/message/content/urlPreviews/UrlImagePreview.tsx @@ -9,7 +9,7 @@ const UrlImagePreview = ({ url }: Pick<UrlPreviewMetadata, 'url'>): ReactElement const { maxHeight: oembedMaxHeight } = useOembedLayout(); return ( - <Box maxHeight={oembedMaxHeight} maxWidth={'100%'}> + <Box maxHeight={oembedMaxHeight} maxWidth='100%'> <MessageGenericPreviewImage className='gallery-item' url={url || ''} /> </Box> ); diff --git a/apps/meteor/client/sidebar/Item/Condensed.stories.tsx b/apps/meteor/client/sidebar/Item/Condensed.stories.tsx index bda4d9769276..915c4646f082 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.stories.tsx @@ -26,7 +26,11 @@ export default { const Template: ComponentStory<typeof Condensed> = (args) => ( <Condensed {...args} - titleIcon={<Box mi='x4'>{<Status.Online />}</Box>} + titleIcon={ + <Box mi='x4'> + <Status.Online /> + </Box> + } avatar={<UserAvatar username='john.doe' size='x16' url='https://via.placeholder.com/16' />} /> ); diff --git a/apps/meteor/client/sidebar/Item/Condensed.tsx b/apps/meteor/client/sidebar/Item/Condensed.tsx index 9a8988a6ed0c..527d38a0879c 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.tsx @@ -48,7 +48,11 @@ const Condensed: FC<CondensedProps> = ({ icon, title = '', avatar, actions, href </Sidebar.Item.Menu> )} </Sidebar.Item.Content> - {actions && <Sidebar.Item.Container>{<Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions>}</Sidebar.Item.Container>} + {actions && ( + <Sidebar.Item.Container> + <Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions> + </Sidebar.Item.Container> + )} </Sidebar.Item> ); }; diff --git a/apps/meteor/client/sidebar/Item/Extended.stories.tsx b/apps/meteor/client/sidebar/Item/Extended.stories.tsx index b621ea3396b4..04c993bc963c 100644 --- a/apps/meteor/client/sidebar/Item/Extended.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.stories.tsx @@ -51,7 +51,11 @@ const Template: ComponentStory<typeof Extended> = (args) => ( </Badge> </Box> } - titleIcon={<Box mi='x4'>{<Status.Online />}</Box>} + titleIcon={ + <Box mi='x4'> + <Status.Online /> + </Box> + } avatar={<UserAvatar username='john.doe' size='x16' url='https://via.placeholder.com/16' />} /> ); diff --git a/apps/meteor/client/sidebar/Item/Extended.tsx b/apps/meteor/client/sidebar/Item/Extended.tsx index a23b55ff10de..c896037d7f3d 100644 --- a/apps/meteor/client/sidebar/Item/Extended.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.tsx @@ -78,7 +78,11 @@ const Extended: VFC<ExtendedProps> = ({ </Sidebar.Item.Wrapper> </Sidebar.Item.Content> </Sidebar.Item.Content> - {actions && <Sidebar.Item.Container>{<Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions>}</Sidebar.Item.Container>} + {actions && ( + <Sidebar.Item.Container> + <Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions> + </Sidebar.Item.Container> + )} </Sidebar.Item> ); }; diff --git a/apps/meteor/client/sidebar/Item/Medium.stories.tsx b/apps/meteor/client/sidebar/Item/Medium.stories.tsx index 9fe712d95f87..d7698356f1b1 100644 --- a/apps/meteor/client/sidebar/Item/Medium.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.stories.tsx @@ -26,7 +26,11 @@ export default { const Template: ComponentStory<typeof Medium> = (args) => ( <Medium {...args} - titleIcon={<Box mi='x4'>{<Status.Online />}</Box>} + titleIcon={ + <Box mi='x4'> + <Status.Online /> + </Box> + } avatar={<UserAvatar username='john.doe' size='x16' url='https://via.placeholder.com/16' />} /> ); diff --git a/apps/meteor/client/sidebar/Item/Medium.tsx b/apps/meteor/client/sidebar/Item/Medium.tsx index 16de09d151f8..2c97b890988f 100644 --- a/apps/meteor/client/sidebar/Item/Medium.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.tsx @@ -46,7 +46,11 @@ const Medium: VFC<MediumProps> = ({ icon, title = '', avatar, actions, href, bad </Sidebar.Item.Menu> )} </Sidebar.Item.Content> - {actions && <Sidebar.Item.Container>{<Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions>}</Sidebar.Item.Container>} + {actions && ( + <Sidebar.Item.Container> + <Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions> + </Sidebar.Item.Container> + )} </Sidebar.Item> ); }; diff --git a/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx b/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx index 6afe313f636d..90d7885b4a62 100644 --- a/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx +++ b/apps/meteor/client/sidebar/header/MatrixFederationSearch/FederatedRoomListItem.tsx @@ -39,7 +39,7 @@ const FederatedRoomListItem: VFC<FederatedRoomListItemProps> = ({ {/* Currently canJoin is only false when the ammount of members is too big. This property will be used in the future in case the matrix room is knock only. When that happens, the check for this should be based on the limit setting. */} {!canJoin && ( - <Box flexShrink={0} color={'danger'} title={t('Currently_we_dont_support_joining_servers_with_this_many_people')}> + <Box flexShrink={0} color='danger' title={t('Currently_we_dont_support_joining_servers_with_this_many_people')}> {t('Cant_join')} </Box> )} diff --git a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx index 5dff5a642ec1..56752d5c3a68 100644 --- a/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx +++ b/apps/meteor/client/sidebar/header/MatrixFederationSearch/MatrixFederationManageServerModal.tsx @@ -56,7 +56,7 @@ const MatrixFederationAddServerModal: VFC<MatrixFederationAddServerModalProps> = const { data, isLoading: isLoadingServerList } = useMatrixServerList(); return ( - <Modal maxHeight={'x600'}> + <Modal maxHeight='x600'> <Modal.Header> <Modal.Title>{t('Manage_servers')}</Modal.Title> <Modal.Close onClick={onClickClose} /> diff --git a/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx b/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx index bf5867df254c..c0abb70b230d 100644 --- a/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx +++ b/apps/meteor/client/sidebar/sections/actions/OmnichannelLivechatToggle.tsx @@ -23,7 +23,7 @@ export const OmnichannelLivechatToggle = (props: Omit<ComponentProps<typeof Side return ( <Sidebar.TopBar.Action {...props} - id={'omnichannel-status-toggle'} + id='omnichannel-status-toggle' data-tooltip={agentAvailable ? t('Turn_off_answer_chats') : t('Turn_on_answer_chats')} success={agentAvailable} icon={agentAvailable ? 'message' : 'message-disabled'} diff --git a/apps/meteor/client/views/account/tokens/AccountTokensTable/AccountTokensTable.tsx b/apps/meteor/client/views/account/tokens/AccountTokensTable/AccountTokensTable.tsx index 4051bb76074f..f736e421df0f 100644 --- a/apps/meteor/client/views/account/tokens/AccountTokensTable/AccountTokensTable.tsx +++ b/apps/meteor/client/views/account/tokens/AccountTokensTable/AccountTokensTable.tsx @@ -47,11 +47,11 @@ const AccountTokensTable = (): ReactElement => { const headers = useMemo( () => [ - <GenericTableHeaderCell key={'name'}>{t('API_Personal_Access_Token_Name')}</GenericTableHeaderCell>, - isMedium && <GenericTableHeaderCell key={'createdAt'}>{t('Created_at')}</GenericTableHeaderCell>, - <GenericTableHeaderCell key={'lastTokenPart'}>{t('Last_token_part')}</GenericTableHeaderCell>, - <GenericTableHeaderCell key={'2fa'}>{t('Two Factor Authentication')}</GenericTableHeaderCell>, - <GenericTableHeaderCell key={'actions'} />, + <GenericTableHeaderCell key='name'>{t('API_Personal_Access_Token_Name')}</GenericTableHeaderCell>, + isMedium && <GenericTableHeaderCell key='createdAt'>{t('Created_at')}</GenericTableHeaderCell>, + <GenericTableHeaderCell key='lastTokenPart'>{t('Last_token_part')}</GenericTableHeaderCell>, + <GenericTableHeaderCell key='2fa'>{t('Two Factor Authentication')}</GenericTableHeaderCell>, + <GenericTableHeaderCell key='actions' />, ].filter(Boolean), [isMedium, t], ); diff --git a/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx b/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx index 1f00584c0b64..09548b3349a9 100644 --- a/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx +++ b/apps/meteor/client/views/admin/cloud/modals/RegisterWorkspaceSetupModal/RegisterWorkspaceSetupStepOneModal.tsx @@ -90,7 +90,7 @@ const RegisterWorkspaceSetupStepOneModal = ({ <Box is='p' fontSize='c1' pis={8}> <Trans i18nKey='RegisterWorkspace_Setup_Terms_Privacy'> I agree with <ExternalLink to='https://rocket.chat/terms'>Terms and Conditions </ExternalLink> - and {''} + and <ExternalLink to='https://rocket.chat/privacy'>Privacy Policy</ExternalLink> </Trans> </Box> diff --git a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx index c318f9fb8a3e..0c826fd1f176 100644 --- a/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx +++ b/apps/meteor/client/views/admin/moderation/ModerationConsoleTable.tsx @@ -74,7 +74,7 @@ const ModerationConsoleTable: FC = () => { const headers = useMemo( () => [ <GenericTableHeaderCell - key={'name'} + key='name' direction={sortDirection} active={sortBy === 'reports.message.u.username'} onClick={setSort} @@ -95,7 +95,7 @@ const ModerationConsoleTable: FC = () => { </GenericTableHeaderCell> ), <GenericTableHeaderCell - key={'reportedMessage'} + key='reportedMessage' direction={sortDirection} active={sortBy === 'reports.description'} onClick={setSort} @@ -103,22 +103,16 @@ const ModerationConsoleTable: FC = () => { > {t('Moderation_Reported_message')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'room'} direction={sortDirection}> + <GenericTableHeaderCell key='room' direction={sortDirection}> {t('Room')} </GenericTableHeaderCell>, - <GenericTableHeaderCell - key={'postdate'} - direction={sortDirection} - active={sortBy === 'reports.ts'} - onClick={setSort} - sort='reports.ts' - > + <GenericTableHeaderCell key='postdate' direction={sortDirection} active={sortBy === 'reports.ts'} onClick={setSort} sort='reports.ts'> {t('Moderation_Report_date')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'reports'} direction={sortDirection} active={sortBy === 'count'} onClick={setSort} sort='count'> + <GenericTableHeaderCell key='reports' direction={sortDirection} active={sortBy === 'count'} onClick={setSort} sort='count'> {t('Moderation_Report_plural')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'actions'} width={'5%'} />, + <GenericTableHeaderCell key='actions' width='5%' />, ], [sortDirection, sortBy, setSort, t, isDesktopOrLarger], ); diff --git a/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx b/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx index bbe841226bed..91224cd8ed3b 100644 --- a/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx +++ b/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx @@ -25,7 +25,7 @@ const CustomRoleUpsellModal: VFC<CustomRoleUpsellModalProps> = ({ onClose }) => variant='warning' icon={null} > - <Modal.HeroImage maxHeight='initial' src={'images/custom-role-upsell-modal.png'} /> + <Modal.HeroImage maxHeight='initial' src='images/custom-role-upsell-modal.png' /> <Box is='h3' fontScale='h3'> {t('Custom_roles_upsell_add_custom_roles_workspace')} </Box> diff --git a/apps/meteor/client/views/admin/permissions/RoleForm.tsx b/apps/meteor/client/views/admin/permissions/RoleForm.tsx index 6fcae6b620b2..346b1064db1f 100644 --- a/apps/meteor/client/views/admin/permissions/RoleForm.tsx +++ b/apps/meteor/client/views/admin/permissions/RoleForm.tsx @@ -42,7 +42,7 @@ const RoleForm = ({ className, editing = false, isProtected = false, isDisabled <Field.Row> <TextInput placeholder={t('Description')} disabled={isDisabled} {...register('description')} /> </Field.Row> - <Field.Hint>{'Leave the description field blank if you dont want to show the role'}</Field.Hint> + <Field.Hint>Leave the description field blank if you dont want to show the role</Field.Hint> </Field> <Field className={className}> <Field.Label>{t('Scope')}</Field.Label> diff --git a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx index 0c36105d89ba..0f7d02bea814 100644 --- a/apps/meteor/client/views/admin/rooms/RoomsTable.tsx +++ b/apps/meteor/client/views/admin/rooms/RoomsTable.tsx @@ -119,14 +119,14 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React const headers = useMemo( () => [ - <GenericTableHeaderCell key={'name'} direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name' w='x200'> + <GenericTableHeaderCell key='name' direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name' w='x200'> {t('Name')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'type'} direction={sortDirection} active={sortBy === 't'} onClick={setSort} sort='t' w='x100'> + <GenericTableHeaderCell key='type' direction={sortDirection} active={sortBy === 't'} onClick={setSort} sort='t' w='x100'> {t('Type')} </GenericTableHeaderCell>, <GenericTableHeaderCell - key={'users'} + key='users' direction={sortDirection} active={sortBy === 'usersCount'} onClick={setSort} @@ -136,20 +136,13 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React {t('Users')} </GenericTableHeaderCell>, mediaQuery && ( - <GenericTableHeaderCell - key={'messages'} - direction={sortDirection} - active={sortBy === 'msgs'} - onClick={setSort} - sort='msgs' - w='x80' - > + <GenericTableHeaderCell key='messages' direction={sortDirection} active={sortBy === 'msgs'} onClick={setSort} sort='msgs' w='x80'> {t('Msgs')} </GenericTableHeaderCell> ), mediaQuery && ( <GenericTableHeaderCell - key={'default'} + key='default' direction={sortDirection} active={sortBy === 'default'} onClick={setSort} @@ -161,7 +154,7 @@ const RoomsTable = ({ reload }: { reload: MutableRefObject<() => void> }): React ), mediaQuery && ( <GenericTableHeaderCell - key={'featured'} + key='featured' direction={sortDirection} active={sortBy === 'featured'} onClick={setSort} diff --git a/apps/meteor/client/views/admin/settings/Section.tsx b/apps/meteor/client/views/admin/settings/Section.tsx index 12ee06c9ad8f..bde10df56d09 100644 --- a/apps/meteor/client/views/admin/settings/Section.tsx +++ b/apps/meteor/client/views/admin/settings/Section.tsx @@ -95,7 +95,7 @@ function Section({ groupId, hasReset = true, sectionName, tabName = '', solo, he children={t('Reset_section_settings')} secondary danger - marginBlockStart={'x16'} + marginBlockStart='x16' data-section={sectionName} onClick={handleResetSectionClick} /> diff --git a/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx b/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx index a27ea9ac1564..978cd02a7f08 100644 --- a/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx +++ b/apps/meteor/client/views/admin/settings/groups/voip/VoipExtensionsPage.tsx @@ -89,7 +89,7 @@ const VoipExtensionsPage = () => { <GenericTableCell withTruncatedText> {username ? ( <Box display='flex' alignItems='center'> - <UserAvatar size={'x28'} username={username} /> + <UserAvatar size='x28' username={username} /> <Box display='flex' mi='x8'> <Box display='flex' flexDirection='column' alignSelf='center'> <Box fontScale='p2m' color='default'> diff --git a/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx b/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx index 768ba65a9b77..c67d408aa2a3 100644 --- a/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx +++ b/apps/meteor/client/views/directory/tabs/users/UsersTable/UsersTable.tsx @@ -36,12 +36,12 @@ const UsersTable = ({ workspace = 'local' }): ReactElement => { const headers = useMemo( () => [ - <GenericTableHeaderCell key={'name'} direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name'> + <GenericTableHeaderCell key='name' direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name'> {t('Name')} </GenericTableHeaderCell>, mediaQuery && canViewFullOtherUserInfo && ( <GenericTableHeaderCell - key={'email'} + key='email' direction={sortDirection} active={sortBy === 'email'} onClick={setSort} diff --git a/apps/meteor/client/views/meet/CallPage.tsx b/apps/meteor/client/views/meet/CallPage.tsx index 8e0fc951e3c5..37cc65870be9 100644 --- a/apps/meteor/client/views/meet/CallPage.tsx +++ b/apps/meteor/client/views/meet/CallPage.tsx @@ -324,7 +324,7 @@ const CallPage: FC<CallPageProps> = ({ size='x124' /> <Box color='white' fontSize={16} margin={15}> - {'Calling...'} + Calling... </Box> <Box style={{ diff --git a/apps/meteor/client/views/meet/MeetPage.tsx b/apps/meteor/client/views/meet/MeetPage.tsx index 1f1f60d5295c..b5378b94dd78 100644 --- a/apps/meteor/client/views/meet/MeetPage.tsx +++ b/apps/meteor/client/views/meet/MeetPage.tsx @@ -118,7 +118,7 @@ const MeetPage = () => { className='rcx-message__avatar' size='x124' /> - <p style={{ color: 'white', fontSize: 16, margin: 15 }}>{'Call Ended!'}</p> + <p style={{ color: 'white', fontSize: 16, margin: 15 }}>Call Ended!</p> <p style={{ color: 'white', diff --git a/apps/meteor/client/views/omnichannel/agents/AgentInfoActions.tsx b/apps/meteor/client/views/omnichannel/agents/AgentInfoActions.tsx index 97dffc4682bd..3a8422be381d 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentInfoActions.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentInfoActions.tsx @@ -47,8 +47,8 @@ const AgentInfoActions = ({ reload }: { reload: () => void }): ReactElement => { return ( <> - <AgentInfoAction key={t('Edit')} title={t('Edit')} label={t('Edit')} onClick={handleEditClick} icon={'edit'} /> - <AgentInfoAction key={t('Remove')} title={t('Remove')} label={t('Remove')} onClick={handleDelete} icon={'trash'} /> + <AgentInfoAction key={t('Edit')} title={t('Edit')} label={t('Edit')} onClick={handleEditClick} icon='edit' /> + <AgentInfoAction key={t('Remove')} title={t('Remove')} label={t('Remove')} onClick={handleDelete} icon='trash' /> </> ); }; diff --git a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx index 7978903afe9c..88cd8ebdf0eb 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessage.tsx @@ -51,7 +51,7 @@ const ContactHistoryMessage: FC<{ <UserAvatar url={message.avatar} username={message.u.username} - size={'x18'} + size='x18' onClick={chat?.userCard.open(message.u.username)} style={{ cursor: 'pointer' }} /> @@ -79,7 +79,7 @@ const ContactHistoryMessage: FC<{ <UserAvatar url={message.avatar} username={message.u.username} - size={'x36'} + size='x36' onClick={chat?.userCard.open(message.u.username)} style={{ cursor: 'pointer' }} /> diff --git a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx index 6cb32e877397..a5821382ac5b 100644 --- a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx +++ b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx @@ -343,7 +343,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen <MaxChats value={value} handler={onChange} - label={'Max_number_of_chats_per_agent'} + label='Max_number_of_chats_per_agent' placeholder='Max_number_of_chats_per_agent_description' /> )} @@ -360,7 +360,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen <VisitorInactivity value={value} handler={onChange} - label={'How_long_to_wait_to_consider_visitor_abandonment_in_seconds'} + label='How_long_to_wait_to_consider_visitor_abandonment_in_seconds' placeholder='Number_in_seconds' /> )} @@ -377,7 +377,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen <AbandonedMessageInput value={value} handler={onChange} - label={'Livechat_abandoned_rooms_closed_custom_message'} + label='Livechat_abandoned_rooms_closed_custom_message' placeholder='Enter_a_custom_message' /> )} @@ -394,8 +394,8 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen <WaitingQueueMessageInput value={value} handler={onChange} - label={'Waiting_queue_message'} - placeholder={'Waiting_queue_message'} + label='Waiting_queue_message' + placeholder='Waiting_queue_message' /> )} /> @@ -412,7 +412,7 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen departmentId={id ?? ''} value={value} handler={onChange} - label={'List_of_departments_for_forward'} + label='List_of_departments_for_forward' /> )} /> diff --git a/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx b/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx index aa92b72310eb..af02cc59d64f 100644 --- a/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx +++ b/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx @@ -62,7 +62,7 @@ const ChatsContextualBar: FC<{ chatReload?: () => void }> = ({ chatReload }) => <> <ContextualbarIcon name='info-circled' /> <ContextualbarTitle>{t('Room_Info')}</ContextualbarTitle> - <ContextualbarAction title={t('View_full_conversation')} name={'new-window'} onClick={openInRoom} /> + <ContextualbarAction title={t('View_full_conversation')} name='new-window' onClick={openInRoom} /> </> )} {bar === 'edit' && ( diff --git a/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx b/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx index 6a51b6fa4b03..d31648aeaae2 100644 --- a/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx +++ b/apps/meteor/client/views/omnichannel/directory/ContactContextualBar.tsx @@ -38,7 +38,7 @@ const ContactContextualBar = () => { const header = useMemo(() => HEADER_OPTIONS[bar] || HEADER_OPTIONS.info, [bar]); return ( - <Contextualbar className={'contextual-bar'}> + <Contextualbar className='contextual-bar'> <ContextualbarHeader> <ContextualbarIcon name={header.icon} /> <ContextualbarTitle>{t(header.title)}</ContextualbarTitle> diff --git a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx index 5aa474893476..89ae577f4bb8 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/CallTable.tsx @@ -60,14 +60,14 @@ const CallTable = () => { <GenericTableHeaderCell key='phone' direction={sortDirection} active={sortBy === 'phone'} onClick={setSort} sort='phone' w='x200'> {t('Phone')} </GenericTableHeaderCell> - <GenericTableHeaderCell key={'queue'} direction={sortDirection} active={sortBy === 'queue'} onClick={setSort} sort='ts' w='x100'> + <GenericTableHeaderCell key='queue' direction={sortDirection} active={sortBy === 'queue'} onClick={setSort} sort='ts' w='x100'> {t('Queue')} </GenericTableHeaderCell> - <GenericTableHeaderCell key={'ts'} direction={sortDirection} active={sortBy === 'ts'} onClick={setSort} sort='ts' w='x200'> + <GenericTableHeaderCell key='ts' direction={sortDirection} active={sortBy === 'ts'} onClick={setSort} sort='ts' w='x200'> {t('Started_At')} </GenericTableHeaderCell> <GenericTableHeaderCell - key={'callDuration'} + key='callDuration' direction={sortDirection} active={sortBy === 'callDuration'} onClick={setSort} diff --git a/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx b/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx index ca86752e7886..a78e7c7546e5 100644 --- a/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx +++ b/apps/meteor/client/views/room/Header/Omnichannel/OmnichannelRoomHeader.tsx @@ -45,7 +45,7 @@ const OmnichannelRoomHeader: FC<OmnichannelRoomHeaderProps> = ({ slots: parentSl start: (!!isMobile || currentRouteName === 'omnichannel-directory' || currentRouteName === 'omnichannel-current-chats') && ( <HeaderToolbox> {isMobile && <BurgerMenu />} - {<BackButton routeName={currentRouteName} />} + <BackButton routeName={currentRouteName} /> </HeaderToolbox> ), posContent: <QuickActions room={room} />, diff --git a/apps/meteor/client/views/room/RoomOpener.tsx b/apps/meteor/client/views/room/RoomOpener.tsx index fc7eba8d7f15..43c2bbed9027 100644 --- a/apps/meteor/client/views/room/RoomOpener.tsx +++ b/apps/meteor/client/views/room/RoomOpener.tsx @@ -29,7 +29,11 @@ const RoomOpener = ({ type, reference }: RoomOpenerProps): ReactElement => { return ( <Suspense fallback={<RoomSkeleton />}> {isLoading && <RoomSkeleton />} - {isSuccess && <RoomProvider rid={data.rid}>{<Room />}</RoomProvider>} + {isSuccess && ( + <RoomProvider rid={data.rid}> + <Room /> + </RoomProvider> + )} {isError && (() => { if (error instanceof RoomNotFoundError) { diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx index 078ea3bd797a..31d327351ae8 100644 --- a/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx +++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/MailExportForm.tsx @@ -139,7 +139,7 @@ const MailExportForm: FC<MailExportFormProps> = ({ onCancel, rid }) => { </Field.Row> </Field> - {errorMessage && <Callout type={'danger'}>{errorMessage}</Callout>} + {errorMessage && <Callout type='danger'>{errorMessage}</Callout>} <ButtonGroup stretch mb='x12'> <Button onClick={onCancel}>{t('Cancel')}</Button> diff --git a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx index bcfe97d6b816..3cb5d5f5ee58 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx +++ b/apps/meteor/client/views/room/contextualBar/Info/RoomInfo/RoomInfo.tsx @@ -77,7 +77,7 @@ const RoomInfo = ({ room, icon, onClickBack, onClickClose, onClickEnterRoom, onC <ContextualbarScrollableContent p='x24'> <InfoPanel> <InfoPanel.Avatar> - <RoomAvatar size={'x332'} room={room} /> + <RoomAvatar size='x332' room={room} /> </InfoPanel.Avatar> <InfoPanel.ActionGroup>{actions}</InfoPanel.ActionGroup> diff --git a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx index 8fa434f55255..7be5f929d12b 100644 --- a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx +++ b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/NotificationPreferencesForm.tsx @@ -111,7 +111,7 @@ const NotificationPreferencesForm = ({ notificationOptions, handlePlaySound }: N )} /> </NotificationByDevice> - <NotificationByDevice device={t('Email')} icon={'mail'}> + <NotificationByDevice device={t('Email')} icon='mail'> <Controller control={control} name='emailAlert' diff --git a/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx b/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx index 1562c469bef9..68e47fd39774 100644 --- a/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx +++ b/apps/meteor/client/views/setupWizard/steps/AdminInfoStep.tsx @@ -28,7 +28,7 @@ const AdminInfoStep = (): ReactElement => { return ( <AdminInfoPage validatePassword={(password): boolean => password.length > 0} - passwordRulesHint={''} + passwordRulesHint='' validateUsername={validateUsername} validateEmail={validateEmail} currentStep={currentStep} diff --git a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx index 1863434fcf4d..501baf796f7d 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx +++ b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx @@ -134,7 +134,7 @@ const TeamsInfo = ({ <ContextualbarScrollableContent p='x24'> <InfoPanel> <InfoPanel.Avatar> - <RoomAvatar size={'x332'} room={room} /> + <RoomAvatar size='x332' room={room} /> </InfoPanel.Avatar> <InfoPanel.ActionGroup>{actions}</InfoPanel.ActionGroup> @@ -148,7 +148,7 @@ const TeamsInfo = ({ </InfoPanel.Section> <InfoPanel.Section> - <InfoPanel.Title title={room.fname || room.name || ''} icon={'team'} /> + <InfoPanel.Title title={room.fname || room.name || ''} icon='team' /> </InfoPanel.Section> <InfoPanel.Section> @@ -163,21 +163,27 @@ const TeamsInfo = ({ {room.description && ( <InfoPanel.Field> <InfoPanel.Label>{t('Description')}</InfoPanel.Label> - <InfoPanel.Text withTruncatedText={false}>{<MarkdownText variant='inline' content={room.description} />}</InfoPanel.Text> + <InfoPanel.Text withTruncatedText={false}> + <MarkdownText variant='inline' content={room.description} /> + </InfoPanel.Text> </InfoPanel.Field> )} {room.announcement && ( <InfoPanel.Field> <InfoPanel.Label>{t('Announcement')}</InfoPanel.Label> - <InfoPanel.Text withTruncatedText={false}>{<MarkdownText variant='inline' content={room.announcement} />}</InfoPanel.Text> + <InfoPanel.Text withTruncatedText={false}> + <MarkdownText variant='inline' content={room.announcement} /> + </InfoPanel.Text> </InfoPanel.Field> )} {room.topic && ( <InfoPanel.Field> <InfoPanel.Label>{t('Topic')}</InfoPanel.Label> - <InfoPanel.Text withTruncatedText={false}>{<MarkdownText variant='inline' content={room.topic} />}</InfoPanel.Text> + <InfoPanel.Text withTruncatedText={false}> + <MarkdownText variant='inline' content={room.topic} /> + </InfoPanel.Text> </InfoPanel.Field> )} diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx index 8a563e94bcc5..efc64a8a4e09 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponsesTable.tsx @@ -113,7 +113,7 @@ const CannedResponsesTable = () => { {t('Created_by')} </GenericTableHeaderCell> <GenericTableHeaderCell - key={'createdAt'} + key='createdAt' direction={sortDirection} active={sortBy === '_createdAt'} onClick={setSort} diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx index 91fe900619f5..87d75baf18c2 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/components/cannedResponseForm.tsx @@ -39,7 +39,7 @@ const CannedResponseForm: FC<{ <Field.Label>{t('Shortcut')}</Field.Label> <TextInput error={errors.shortcut} - name={'shortcut'} + name='shortcut' placeholder={`!${t('shortcut_name')}`} onChange={handleShortcut} value={shortcut} diff --git a/apps/meteor/ee/client/omnichannel/priorities/PriorityEditForm.tsx b/apps/meteor/ee/client/omnichannel/priorities/PriorityEditForm.tsx index 5f87ba7b4661..81d0fd72a2e6 100644 --- a/apps/meteor/ee/client/omnichannel/priorities/PriorityEditForm.tsx +++ b/apps/meteor/ee/client/omnichannel/priorities/PriorityEditForm.tsx @@ -80,7 +80,7 @@ const PriorityEditForm = ({ data, onSave, onCancel }: PriorityEditFormProps): Re rules={{ required: t('The_field_is_required', t('Name')), validate: (v) => v?.trim() !== '' }} render={({ field: { value, onChange } }): ReactElement => ( <StringSettingInput - _id={''} + _id='' disabled={isSaving} error={errors.name?.message} label={`${t('Name')}*`} diff --git a/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx b/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx index d643a1c290e6..7448a13645d1 100644 --- a/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx +++ b/apps/meteor/ee/client/omnichannel/slaPolicies/SlaTable.tsx @@ -78,7 +78,7 @@ const SlaTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { > {t('Estimated_wait_time')} </GenericTableHeaderCell> - <GenericTableHeaderCell key={'remove'} w='x60'> + <GenericTableHeaderCell key='remove' w='x60'> {t('Remove')} </GenericTableHeaderCell> </> diff --git a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx index b354e52aa35b..2cc8b55c78f1 100644 --- a/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/units/UnitsTable.tsx @@ -57,11 +57,11 @@ const UnitsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { ); const headers = ( <> - <GenericTableHeaderCell key={'name'} direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name'> + <GenericTableHeaderCell key='name' direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name'> {t('Name')} </GenericTableHeaderCell> <GenericTableHeaderCell - key={'visibility'} + key='visibility' direction={sortDirection} active={sortBy === 'visibility'} onClick={setSort} @@ -69,7 +69,7 @@ const UnitsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => { > {t('Visibility')} </GenericTableHeaderCell> - <GenericTableHeaderCell key={'remove'} w='x60'> + <GenericTableHeaderCell key='remove' w='x60'> {t('Remove')} </GenericTableHeaderCell> </> diff --git a/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx b/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx index b3f5fde30912..fe480928e543 100644 --- a/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx +++ b/apps/meteor/ee/client/views/account/deviceManagement/DeviceManagementAccountTable/DeviceManagementAccountTable.tsx @@ -36,17 +36,17 @@ const DeviceManagementAccountTable = (): ReactElement => { const headers = useMemo( () => [ - <GenericTableHeaderCell key={'client'} direction={sortDirection} active={sortBy === 'client'} onClick={setSort} sort={'client'}> + <GenericTableHeaderCell key='client' direction={sortDirection} active={sortBy === 'client'} onClick={setSort} sort='client'> {t('Client')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'os'} direction={sortDirection} active={sortBy === 'os'} onClick={setSort} sort={'os'}> + <GenericTableHeaderCell key='os' direction={sortDirection} active={sortBy === 'os'} onClick={setSort} sort='os'> {t('OS')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'loginAt'} direction={sortDirection} active={sortBy === 'loginAt'} onClick={setSort} sort={'loginAt'}> + <GenericTableHeaderCell key='loginAt' direction={sortDirection} active={sortBy === 'loginAt'} onClick={setSort} sort='loginAt'> {t('Last_login')} </GenericTableHeaderCell>, - mediaQuery && <GenericTableHeaderCell key={'_id'}>{t('Device_ID')}</GenericTableHeaderCell>, - <GenericTableHeaderCell key={'logout'} />, + mediaQuery && <GenericTableHeaderCell key='_id'>{t('Device_ID')}</GenericTableHeaderCell>, + <GenericTableHeaderCell key='logout' />, ], [t, mediaQuery, sortDirection, sortBy, setSort], ); diff --git a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx index db1c78f53ced..428242f987d0 100644 --- a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx +++ b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminTable/DeviceManagementAdminTable.tsx @@ -52,24 +52,24 @@ const DeviceManagementAdminTable = ({ reloadRef }: { reloadRef: MutableRefObject const headers = useMemo( () => [ - <GenericTableHeaderCell key={'client'} direction={sortDirection} active={sortBy === 'client'} onClick={setSort} sort='client'> + <GenericTableHeaderCell key='client' direction={sortDirection} active={sortBy === 'client'} onClick={setSort} sort='client'> {t('Client')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'rcVersion'}>{t('Version')}</GenericTableHeaderCell>, - <GenericTableHeaderCell key={'os'} direction={sortDirection} active={sortBy === 'os'} onClick={setSort} sort='os'> + <GenericTableHeaderCell key='rcVersion'>{t('Version')}</GenericTableHeaderCell>, + <GenericTableHeaderCell key='os' direction={sortDirection} active={sortBy === 'os'} onClick={setSort} sort='os'> {t('OS')} </GenericTableHeaderCell>, - <GenericTableHeaderCell key={'username'} direction={sortDirection} active={sortBy === 'username'} onClick={setSort} sort='username'> + <GenericTableHeaderCell key='username' direction={sortDirection} active={sortBy === 'username'} onClick={setSort} sort='username'> {t('User')} </GenericTableHeaderCell>, mediaQuery && ( - <GenericTableHeaderCell key={'loginAt'} direction={sortDirection} active={sortBy === 'loginAt'} onClick={setSort} sort='loginAt'> + <GenericTableHeaderCell key='loginAt' direction={sortDirection} active={sortBy === 'loginAt'} onClick={setSort} sort='loginAt'> {t('Last_login')} </GenericTableHeaderCell> ), - mediaQuery && <GenericTableHeaderCell key={'_id'}>{t('Device_ID')}</GenericTableHeaderCell>, - mediaQuery && <GenericTableHeaderCell key={'ip'}>{t('IP_Address')}</GenericTableHeaderCell>, - <GenericTableHeaderCell width={'5%'} key='menu' />, + mediaQuery && <GenericTableHeaderCell key='_id'>{t('Device_ID')}</GenericTableHeaderCell>, + mediaQuery && <GenericTableHeaderCell key='ip'>{t('IP_Address')}</GenericTableHeaderCell>, + <GenericTableHeaderCell width='5%' key='menu' />, ], [t, mediaQuery, setSort, sortDirection, sortBy], ); diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx index 83c208dd0b2e..e34c34d62816 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/channels/ChannelsOverview.tsx @@ -69,7 +69,7 @@ const ChannelsOverview = (): ReactElement => { <Table> <TableHead> <TableRow> - <TableCell>{'#'}</TableCell> + <TableCell>#</TableCell> <TableCell>{t('Channel')}</TableCell> <TableCell>{t('Created')}</TableCell> <TableCell>{t('Last_active')}</TableCell> diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx index d151484116f0..1a7c0a0aeeee 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/messages/MessagesPerChannelSection.tsx @@ -182,7 +182,7 @@ const MessagesPerChannelSection = (): ReactElement => { <Table> <TableHead> <TableRow> - <TableCell>{'#'}</TableCell> + <TableCell>#</TableCell> <TableCell>{t('Channel')}</TableCell> <TableCell align='end'>{t('Number_of_messages')}</TableCell> </TableRow> diff --git a/apps/meteor/tests/e2e/.eslintrc.json b/apps/meteor/tests/e2e/.eslintrc.json index df41734e7908..f22ce1e25a79 100644 --- a/apps/meteor/tests/e2e/.eslintrc.json +++ b/apps/meteor/tests/e2e/.eslintrc.json @@ -1,8 +1,8 @@ { "root": true, - "extends": ["@rocket.chat/eslint-config/original", "prettier", "plugin:@typescript-eslint/recommended"], + "extends": ["@rocket.chat/eslint-config/original", "@rocket.chat/eslint-config/react", "prettier", "plugin:@typescript-eslint/recommended"], "parser": "@typescript-eslint/parser", - "plugins": ["react", "react-hooks", "prettier", "testing-library", "anti-trojan-source", "no-floating-promise"], + "plugins": ["prettier", "testing-library", "anti-trojan-source", "no-floating-promise"], "rules": { "@typescript-eslint/no-unused-vars": [ "error", diff --git a/ee/packages/pdf-worker/.eslintrc.json b/ee/packages/pdf-worker/.eslintrc.json index ea50ca58bed9..98b4582994d2 100644 --- a/ee/packages/pdf-worker/.eslintrc.json +++ b/ee/packages/pdf-worker/.eslintrc.json @@ -1,5 +1,5 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "parser": "@typescript-eslint/parser", "ignorePatterns": ["**/dist"] } diff --git a/ee/packages/ui-theming/.eslintrc.json b/ee/packages/ui-theming/.eslintrc.json index a83aeda48e66..4c413c4080b3 100644 --- a/ee/packages/ui-theming/.eslintrc.json +++ b/ee/packages/ui-theming/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "ignorePatterns": ["**/dist"] } diff --git a/ee/packages/ui-theming/src/PaletteStyleTag.tsx b/ee/packages/ui-theming/src/PaletteStyleTag.tsx index a63e2a20b766..8aa3f2c04ce2 100644 --- a/ee/packages/ui-theming/src/PaletteStyleTag.tsx +++ b/ee/packages/ui-theming/src/PaletteStyleTag.tsx @@ -1,4 +1,3 @@ -import type { ReactElement } from 'react'; import { memo } from 'react'; import { createPortal } from 'react-dom'; @@ -9,7 +8,7 @@ import { darkPalette } from './paletteDark'; import { useThemeMode } from './hooks/useThemeMode'; import { useCreateStyleContainer } from './hooks/useCreateStyleContainer'; -export const PaletteStyleTag = memo((): ReactElement | null => { +export const PaletteStyleTag = memo(function PaletteStyleTag() { const [, , theme] = useThemeMode(); const palette = diff --git a/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx b/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx index 02b9ee090a49..9af4d3e80f7b 100644 --- a/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx +++ b/ee/packages/ui-theming/src/SidebarPaletteStyleTag.tsx @@ -7,7 +7,7 @@ import { darkPalette } from './paletteDark'; import { convertToCss } from './helpers/convertToCss'; import { useCreateStyleContainer } from './hooks/useCreateStyleContainer'; -export const SidebarPaletteStyleTag = memo((): ReactElement | null => { +export const SidebarPaletteStyleTag = memo(function SidebarPaletteStyleTag(): ReactElement | null { // Commented code below: sidebar palette currently the same in both themes. // const [, , theme] = useThemeMode(); diff --git a/ee/packages/ui-theming/src/hooks/useThemeMode.ts b/ee/packages/ui-theming/src/hooks/useThemeMode.ts index 84deebb28374..a467345f810a 100644 --- a/ee/packages/ui-theming/src/hooks/useThemeMode.ts +++ b/ee/packages/ui-theming/src/hooks/useThemeMode.ts @@ -1,6 +1,6 @@ import { useDarkMode } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; type ThemeMode = 'light' | 'dark' | 'auto'; @@ -15,8 +15,15 @@ export const useThemeMode = (): [ThemeMode, (value: ThemeMode) => () => void, 'l const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences'); - const setTheme = (value: ThemeMode): (() => void) => - useCallback(() => saveUserPreferences({ data: { themeAppearence: value } }), [value]); + const [updaters] = useState( + (): Record<ThemeMode, () => void> => ({ + light: () => saveUserPreferences({ data: { themeAppearence: 'light' } }), + dark: () => saveUserPreferences({ data: { themeAppearence: 'dark' } }), + auto: () => saveUserPreferences({ data: { themeAppearence: 'auto' } }), + }), + ); + + const setTheme = useCallback((value: ThemeMode): (() => void) => updaters[value], [updaters]); return [theme, setTheme, useDarkMode(theme === 'auto' ? undefined : theme === 'dark') ? 'dark' : 'light']; }; diff --git a/package.json b/package.json index a47523eefcea..0465f5ff5607 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@types/chart.js": "^2.9.37", "@types/js-yaml": "^4.0.5", "husky": "^7.0.4", - "turbo": "~1.10.7" + "turbo": "latest" }, "workspaces": [ "apps/*", diff --git a/packages/eslint-config/react.js b/packages/eslint-config/react.js new file mode 100644 index 000000000000..84a784cb7733 --- /dev/null +++ b/packages/eslint-config/react.js @@ -0,0 +1,32 @@ +/** @type {import('eslint').ESLint.ConfigData} */ +const config = { + plugins: ['react', 'react-hooks'], + rules: { + 'react-hooks/exhaustive-deps': 'error', + 'react-hooks/rules-of-hooks': 'error', + 'react/display-name': 'error', + 'react/jsx-curly-brace-presence': 'error', + 'react/jsx-fragments': ['error', 'syntax'], + 'react/jsx-key': ['error', { checkFragmentShorthand: true, checkKeyMustBeforeSpread: true, warnOnDuplicates: true }], + 'react/jsx-no-undef': 'error', + 'react/jsx-uses-react': 'error', + 'react/jsx-uses-vars': 'error', + 'react/no-multi-comp': 'error', + }, + settings: { + react: { + version: 'detect', + }, + }, + overrides: [ + { + files: ['**/*.stories.js', '**/*.stories.jsx', '**/*.stories.ts', '**/*.stories.tsx', '**/*.spec.tsx'], + rules: { + 'react/display-name': 'off', + 'react/no-multi-comp': 'off', + }, + }, + ], +}; + +module.exports = config; diff --git a/packages/fuselage-ui-kit/.eslintrc.js b/packages/fuselage-ui-kit/.eslintrc.js index 72e21b1a70e0..07c7ef53e979 100644 --- a/packages/fuselage-ui-kit/.eslintrc.js +++ b/packages/fuselage-ui-kit/.eslintrc.js @@ -1,3 +1,6 @@ -module.exports = { - extends: '@rocket.chat/eslint-config', +/** @type {import('eslint').ESLint.ConfigData} */ +const config = { + extends: ['@rocket.chat/eslint-config', '@rocket.chat/eslint-config/react'], }; + +module.exports = config; diff --git a/packages/gazzodown/.eslintrc.json b/packages/gazzodown/.eslintrc.json index f51d169592b8..35f767f96c01 100644 --- a/packages/gazzodown/.eslintrc.json +++ b/packages/gazzodown/.eslintrc.json @@ -3,6 +3,7 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", diff --git a/packages/mock-providers/.eslintrc.json b/packages/mock-providers/.eslintrc.json index a83aeda48e66..4c413c4080b3 100644 --- a/packages/mock-providers/.eslintrc.json +++ b/packages/mock-providers/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "ignorePatterns": ["**/dist"] } diff --git a/packages/ui-client/.eslintrc.json b/packages/ui-client/.eslintrc.json index ccfb12f9b975..d5db8560f3e8 100644 --- a/packages/ui-client/.eslintrc.json +++ b/packages/ui-client/.eslintrc.json @@ -3,13 +3,14 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", "plugin:storybook/recommended" ], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"], + "plugins": ["@typescript-eslint", "prettier"], "rules": { "func-call-spacing": "off", "import/named": "error", @@ -33,23 +34,13 @@ "no-useless-constructor": "off", "no-use-before-define": "off", "prefer-arrow-callback": ["error", { "allowNamedFunctions": true }], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn" + "prettier/prettier": 2 }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "ignorePatterns": ["**/dist"], @@ -69,12 +60,6 @@ ], "@typescript-eslint/prefer-optional-chain": "warn" } - }, - { - "files": ["*.stories.tsx"], - "rules": { - "react/no-multi-comp": "off" - } - } + } ] } diff --git a/packages/ui-composer/.eslintrc.json b/packages/ui-composer/.eslintrc.json index 1fc7e5497093..3a40997760d0 100644 --- a/packages/ui-composer/.eslintrc.json +++ b/packages/ui-composer/.eslintrc.json @@ -3,13 +3,14 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", "plugin:storybook/recommended" ], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"], + "plugins": ["@typescript-eslint", "prettier"], "rules": { "func-call-spacing": "off", "import/named": "error", @@ -33,23 +34,13 @@ "no-useless-constructor": "off", "no-use-before-define": "off", "prefer-arrow-callback": ["error", { "allowNamedFunctions": true }], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn" + "prettier/prettier": 2 }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "ignorePatterns": ["**/dist"], @@ -70,12 +61,6 @@ ], "@typescript-eslint/prefer-optional-chain": "warn" } - }, - { - "files": ["*.stories.tsx"], - "rules": { - "react/no-multi-comp": "off" - } } ] } diff --git a/packages/ui-contexts/.eslintrc.json b/packages/ui-contexts/.eslintrc.json index 5b4e017cb820..5fe546755bb7 100644 --- a/packages/ui-contexts/.eslintrc.json +++ b/packages/ui-contexts/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config", "plugin:react-hooks/recommended"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react", "plugin:react-hooks/recommended"], "ignorePatterns": ["**/dist"] } diff --git a/packages/ui-video-conf/.eslintrc.json b/packages/ui-video-conf/.eslintrc.json index 662dc3790993..cbaa27e73b96 100644 --- a/packages/ui-video-conf/.eslintrc.json +++ b/packages/ui-video-conf/.eslintrc.json @@ -3,13 +3,14 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", "@rocket.chat/eslint-config/original", + "@rocket.chat/eslint-config/react", "prettier", "plugin:anti-trojan-source/recommended", "plugin:react/jsx-runtime", "plugin:storybook/recommended" ], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"], + "plugins": ["@typescript-eslint", "prettier"], "rules": { "func-call-spacing": "off", "import/named": "error", @@ -33,23 +34,13 @@ "no-useless-constructor": "off", "no-use-before-define": "off", "prefer-arrow-callback": ["error", { "allowNamedFunctions": true }], - "prettier/prettier": 2, - "react/display-name": "error", - "react/jsx-uses-vars": "error", - "react/jsx-no-undef": "error", - "react/jsx-fragments": ["error", "syntax"], - "react/no-multi-comp": "error", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn" + "prettier/prettier": 2 }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".ts", ".tsx"] } - }, - "react": { - "version": "detect" } }, "ignorePatterns": ["**/dist"], @@ -70,12 +61,6 @@ ], "@typescript-eslint/prefer-optional-chain": "warn" } - }, - { - "files": ["*.stories.tsx"], - "rules": { - "react/no-multi-comp": "off" - } - } + } ] } diff --git a/packages/web-ui-registration/.eslintrc.json b/packages/web-ui-registration/.eslintrc.json index a83aeda48e66..4c413c4080b3 100644 --- a/packages/web-ui-registration/.eslintrc.json +++ b/packages/web-ui-registration/.eslintrc.json @@ -1,4 +1,4 @@ { - "extends": ["@rocket.chat/eslint-config"], + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"], "ignorePatterns": ["**/dist"] } diff --git a/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx b/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx index 2c81b79f8694..9a23039f9336 100644 --- a/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx +++ b/packages/web-ui-registration/src/components/LoginSwitchLanguageFooter.tsx @@ -29,7 +29,7 @@ const LoginSwitchLanguageFooter = (): ReactElement | null => { return Array.from(potentialSuggestions).filter( (language) => language && language !== currentLanguage && Boolean(languages.find(({ key }) => key === language)), ); - }, [serverLanguage, browserLanguage, currentLanguage]); + }, [serverLanguage, currentLanguage, languages]); const handleSwitchLanguageClick = (language: string) => (): void => { loadLanguage(language); diff --git a/yarn.lock b/yarn.lock index f9b00db29e95..ecde951e5d8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36255,7 +36255,7 @@ __metadata: "@types/chart.js": ^2.9.37 "@types/js-yaml": ^4.0.5 husky: ^7.0.4 - turbo: ~1.10.7 + turbo: latest languageName: unknown linkType: soft @@ -39671,58 +39671,58 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-darwin-64@npm:1.10.7" +"turbo-darwin-64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-darwin-64@npm:1.10.8" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-darwin-arm64@npm:1.10.7" +"turbo-darwin-arm64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-darwin-arm64@npm:1.10.8" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-linux-64@npm:1.10.7" +"turbo-linux-64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-linux-64@npm:1.10.8" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-linux-arm64@npm:1.10.7" +"turbo-linux-arm64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-linux-arm64@npm:1.10.8" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-windows-64@npm:1.10.7" +"turbo-windows-64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-windows-64@npm:1.10.8" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.10.7": - version: 1.10.7 - resolution: "turbo-windows-arm64@npm:1.10.7" +"turbo-windows-arm64@npm:1.10.8": + version: 1.10.8 + resolution: "turbo-windows-arm64@npm:1.10.8" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"turbo@npm:~1.10.7": - version: 1.10.7 - resolution: "turbo@npm:1.10.7" +"turbo@npm:latest": + version: 1.10.8 + resolution: "turbo@npm:1.10.8" dependencies: - turbo-darwin-64: 1.10.7 - turbo-darwin-arm64: 1.10.7 - turbo-linux-64: 1.10.7 - turbo-linux-arm64: 1.10.7 - turbo-windows-64: 1.10.7 - turbo-windows-arm64: 1.10.7 + turbo-darwin-64: 1.10.8 + turbo-darwin-arm64: 1.10.8 + turbo-linux-64: 1.10.8 + turbo-linux-arm64: 1.10.8 + turbo-windows-64: 1.10.8 + turbo-windows-arm64: 1.10.8 dependenciesMeta: turbo-darwin-64: optional: true @@ -39738,7 +39738,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: 58329caf13b5fef284ccfc21bd1f841023122371d75d2202be809a385aade84fd886cf5c2093323c115c497d503c9c34e1f7ae09e13d06d1dcb4b649883f60dd + checksum: 9fd2f9b17461a688e200329c4d910969e828b22c375e88759cd0e59f96db7aac115d289ed20af42db23030b4a4ae0334d1a37bf0a5caf2fdc8a618476742238f languageName: node linkType: hard From 5b3b7c55e072e4ba1f4c53cff60fba43d4ceb010 Mon Sep 17 00:00:00 2001 From: Hugo Costa <hugocarreiracosta@gmail.com> Date: Wed, 19 Jul 2023 12:34:04 -0300 Subject: [PATCH 129/149] test: remove unnecessary and flaky e2e test (#29843) --- .../tests/e2e/engagement-dashboard.spec.ts | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 apps/meteor/tests/e2e/engagement-dashboard.spec.ts diff --git a/apps/meteor/tests/e2e/engagement-dashboard.spec.ts b/apps/meteor/tests/e2e/engagement-dashboard.spec.ts deleted file mode 100644 index a6389e62dfa2..000000000000 --- a/apps/meteor/tests/e2e/engagement-dashboard.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { IS_EE } from './config/constants'; -import { Users } from './fixtures/userStates'; -import { test, expect } from './utils/test'; - -test.skip(!IS_EE, 'Engagement Dashboard > Enterprise Only'); - -test.use({ storageState: Users.admin.state }); - -test.describe('engagement-dashboard', () => { - test.beforeEach(async ({ page }) => { - await page.goto('/admin/engagement-dashboard'); - await page.route('**/api/v1/engagement-dashboard/**', (route) => route.abort()); - }); - test('expect to trigger fallback error component', async ({ page }) => { - await test.step('expect to show 4 fallback errors components inside widget at Users Tab', async () => { - await expect(page.locator('role=tab[name="Users"][selected]')).toBeVisible(); - - await page.waitForSelector('[data-qa="EngagementDashboardCardErrorBoundary"]'); - await expect(page.locator('[data-qa="EngagementDashboardCardErrorBoundary"]')).toHaveCount(4); - }); - - await test.step('expect to show 2 fallback errors components inside widget at Messages Tab', async () => { - await page.locator('role=tab[name="Messages"]').click(); - await expect(page.locator('role=tab[name="Messages"][selected]')).toBeVisible(); - - await page.waitForSelector('[data-qa="EngagementDashboardCardErrorBoundary"]'); - await expect(page.locator('[data-qa="EngagementDashboardCardErrorBoundary"]')).toHaveCount(2); - }); - - await test.step('expect to show a fallback error component inside widget at Channels Tab', async () => { - await page.locator('role=tab[name="Channels"]').click(); - await expect(page.locator('role=tab[name="Channels"][selected]')).toBeVisible(); - - await page.waitForSelector('[data-qa="EngagementDashboardCardErrorBoundary"]'); - await expect(page.locator('[data-qa="EngagementDashboardCardErrorBoundary"]')).toBeVisible(); - }); - }); -}); From 4c26133d506e8a2b63436036ee40b83a8ef8d2e1 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Wed, 19 Jul 2023 17:22:52 -0300 Subject: [PATCH 130/149] test: use jest as the default unit test for client code (#29864) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/meteor/.mocharc.client.js | 12 +- .../variants/ThreadMessagePreview.spec.tsx | 99 --------------- .../variants/room/RoomMessageContent.spec.tsx | 88 ------------- .../hooks/useAutoTranslate.spec.ts | 117 ------------------ .../body/composer/MessageComposer.spec.ts | 14 --- apps/meteor/jest.config.ts | 7 +- ee/packages/api-client/jest.config.ts | 1 + 7 files changed, 3 insertions(+), 335 deletions(-) delete mode 100644 apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx delete mode 100644 apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx delete mode 100644 apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts delete mode 100644 apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js index 029564d7729a..16e771dfbc60 100644 --- a/apps/meteor/.mocharc.client.js +++ b/apps/meteor/.mocharc.client.js @@ -32,15 +32,5 @@ module.exports = { timeout: 5000, exit: false, slow: 200, - spec: [ - 'client/**/*.spec.{ts,tsx}', - 'tests/unit/client/**/*.spec.{ts,tsx}', - 'tests/unit/lib/**/*.tests.ts', - 'tests/unit/client/**/*.test.ts', - ], - exclude: [ - 'client/hooks/*.spec.{ts,tsx}', - 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}', - 'client/components/message/content/reactions/**.spec.{ts,tsx}', - ], + spec: ['tests/unit/client/**/*.spec.{ts,tsx}', 'tests/unit/lib/**/*.tests.ts', 'tests/unit/client/**/*.test.ts'], }; diff --git a/apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx b/apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx deleted file mode 100644 index e5deb61442a5..000000000000 --- a/apps/meteor/client/components/message/variants/ThreadMessagePreview.spec.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import type { IMessage, IThreadMessage } from '@rocket.chat/core-typings'; -import { QueryClientProvider } from '@tanstack/react-query'; -import { render, screen } from '@testing-library/react'; -import { expect } from 'chai'; -import proxyquire from 'proxyquire'; -import type { ReactNode } from 'react'; -import React from 'react'; - -import FakeRoomProvider from '../../../../tests/mocks/client/FakeRoomProvider'; -import RouterContextMock from '../../../../tests/mocks/client/RouterContextMock'; -import { createFakeMessageWithMd } from '../../../../tests/mocks/data'; -import { queryClient } from '../../../lib/queryClient'; -import type * as ThreadMessagePreviewModule from './ThreadMessagePreview'; - -describe('ThreadMessagePreview', () => { - const fakeMessage = createFakeMessageWithMd<IThreadMessage>({ - msg: 'message', - }); - - const loadMock = (stubs?: Record<string, unknown>) => { - return proxyquire.noCallThru().load<typeof ThreadMessagePreviewModule>('./ThreadMessagePreview', { - '../../../views/room/MessageList/hooks/useParentMessage': { - useParentMessage: () => '', - }, - '../../../views/room/MessageList/hooks/useMessageBody': { - useMessageBody: () => <p>Parent Message</p>, - }, - '../../../../app/ui-utils/client': { - MessageTypes: { - getType: () => false, - }, - }, - './threadPreview/ThreadMessagePreviewBody': ({ message }: { message: IMessage }) => <span>{message.msg}</span>, - ...stubs, - }).default; - }; - - const ProvidersMock = ({ children }: { children: ReactNode }) => { - return ( - <QueryClientProvider client={queryClient}> - <RouterContextMock> - <FakeRoomProvider>{children}</FakeRoomProvider> - </RouterContextMock> - </QueryClientProvider> - ); - }; - - it('should render the message when exists', () => { - const ThreadMessagePreview = loadMock(); - - render(<ThreadMessagePreview message={fakeMessage} sequential={true} showUserAvatar={true} />, { wrapper: ProvidersMock }); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - }); - - it('should render ignored message', () => { - const ThreadMessagePreview = loadMock(); - - const message = { ...fakeMessage, ignored: true }; - render(<ThreadMessagePreview message={message} sequential={true} showUserAvatar={true} />, { wrapper: ProvidersMock }); - - expect(screen.getByText('Message_Ignored')).to.exist; - }); - - it('should render parent message', () => { - const ThreadMessagePreview = loadMock({ - '../../../views/room/MessageList/hooks/useParentMessage': { - useParentMessage: () => ({ - isSuccess: true, - }), - }, - }); - - render(<ThreadMessagePreview message={fakeMessage} sequential={false} showUserAvatar={true} />, { wrapper: ProvidersMock }); - - expect(screen.getByText('Parent Message')).to.exist; - }); - - it('should render parent system message', () => { - const ThreadMessagePreview = loadMock({ - '../../../views/room/MessageList/hooks/useParentMessage': { - useParentMessage: () => ({ - isSuccess: true, - }), - }, - '../../../../app/ui-utils/client': { - MessageTypes: { - getType: () => ({ - message: 'System Message', - }), - }, - }, - }); - - render(<ThreadMessagePreview message={fakeMessage} sequential={false} showUserAvatar={true} />, { wrapper: ProvidersMock }); - - expect(screen.getByText('System Message')).to.exist; - }); -}); diff --git a/apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx b/apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx deleted file mode 100644 index 49d4426df695..000000000000 --- a/apps/meteor/client/components/message/variants/room/RoomMessageContent.spec.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { QueryClientProvider } from '@tanstack/react-query'; -import { render, screen } from '@testing-library/react'; -import { expect } from 'chai'; -import proxyquire from 'proxyquire'; -import type { ReactNode } from 'react'; -import React, { useMemo } from 'react'; - -import FakeChatProvider from '../../../../../tests/mocks/client/FakeChatProvider'; -import FakeRoomProvider from '../../../../../tests/mocks/client/FakeRoomProvider'; -import RouterContextMock from '../../../../../tests/mocks/client/RouterContextMock'; -import { createFakeMessageWithMd } from '../../../../../tests/mocks/data'; -import { UserPresenceContext } from '../../../../contexts/UserPresenceContext'; -import { queryClient } from '../../../../lib/queryClient'; -import type * as RoomMessageContentModule from './RoomMessageContent'; - -describe('RoomMessageContent', () => { - const fakeMessage = createFakeMessageWithMd({ - msg: 'message', - }); - - const { default: RoomMessageContent } = proxyquire.noCallThru().load<typeof RoomMessageContentModule>('./RoomMessageContent', { - '../../content/UiKitSurface': () => null, - '../../content/Attachments': () => null, - '../../MessageContentBody': () => fakeMessage.msg, - '../../content/DiscussionMetrics': () => null, - '../../content/MessageActions': () => null, - '../../hooks/useNormalizedMessage': { useNormalizedMessage: (args: any) => ({ md: fakeMessage.md, ...args }) }, - }); - - const ProvidersMock = ({ children }: { children: ReactNode }) => { - return ( - <QueryClientProvider client={queryClient}> - <RouterContextMock> - <FakeRoomProvider> - <FakeChatProvider> - <UserPresenceContext.Provider - value={useMemo( - () => ({ - queryUserData: () => ({ - subscribe: () => () => undefined, - get: () => undefined, - }), - }), - [], - )} - > - {children} - </UserPresenceContext.Provider> - </FakeChatProvider> - </FakeRoomProvider> - </RouterContextMock> - </QueryClientProvider> - ); - }; - - it('should render the message when exists', () => { - render(<RoomMessageContent message={fakeMessage} all={false} mention={false} unread={false} />, { - wrapper: ProvidersMock, - }); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - }); - - it('should render the message when has an empty message blocks', () => { - render(<RoomMessageContent message={{ ...fakeMessage, blocks: [] }} all={false} mention={false} unread={false} />, { - wrapper: ProvidersMock, - }); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - }); - - it('should render the message when replies is undefined', () => { - render( - <RoomMessageContent - message={{ ...fakeMessage, replies: undefined, tcount: 0, tlm: fakeMessage.ts }} - all={false} - mention={false} - unread={false} - />, - { - wrapper: ProvidersMock, - }, - ); - - expect(screen.getByText(fakeMessage.msg)).to.exist; - expect(screen.getByTitle('Replies')).to.include.text('0'); - }); -}); diff --git a/apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts b/apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts deleted file mode 100644 index 707ef7543276..000000000000 --- a/apps/meteor/client/views/room/MessageList/hooks/useAutoTranslate.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import { expect } from 'chai'; -import proxyquire from 'proxyquire'; - -const COMPONENT_PATH = './useAutoTranslate'; -const defaultConfig = { - '@rocket.chat/ui-contexts': { - useSetting: () => true, - }, - '../../../../../app/autotranslate/client': { - AutoTranslate: { - getLanguage: () => 'lang', - }, - }, - '../../../../lib/rooms/roomCoordinator': { - roomCoordinator: { - isLivechatRoom: () => false, - }, - }, -}; - -describe('room/MessageList/hooks/useAutoTranslate', () => { - it('should return enabled false and undefined language if no subscription and setting disabled', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '@rocket.chat/ui-contexts': { - useSetting: () => false, - }, - }); - - const { result } = renderHook(() => useAutoTranslate()); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.undefined; - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); - - it('should return enabled false and undefined language if no subscription', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate()); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.undefined; - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); - - it('should return enabled true and the auto translate language if has subscription', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: true, autoTranslateLanguage: 'lang', u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(true); - expect(result.current.autoTranslateLanguage).to.be.equal('lang'); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(true); - }); - - it('should return enabled true and the default auto translate language if is livechat room', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, { - ...defaultConfig, - '../../../../../app/autotranslate/client': { - AutoTranslate: { - getLanguage: () => 'default', - }, - }, - '../../../../lib/rooms/roomCoordinator': { - roomCoordinator: { - isLivechatRoom: () => true, - }, - }, - }); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: false, autoTranslateLanguage: 'default', u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(true); - expect(result.current.autoTranslateLanguage).to.be.equal('default'); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { default: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { default: 'translated' } })).to.be.equal(true); - }); - - it('should return enabled false if no auto translate language', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: true, autoTranslateLanguage: undefined, u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.equal(undefined); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); - - it('should return enabled false and language undefined if auto translate is false and has auto translate language', () => { - const { useAutoTranslate } = proxyquire.noCallThru().load(COMPONENT_PATH, defaultConfig); - - const { result } = renderHook(() => useAutoTranslate({ autoTranslate: false, autoTranslateLanguage: 'lang', u: { _id: 1 } })); - - expect(result.current.autoTranslateEnabled).to.be.equal(false); - expect(result.current.autoTranslateLanguage).to.be.equal(undefined); - - expect(result.current.showAutoTranslate({ u: { _id: 2 } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 1 }, translations: { lang: 'translated' } })).to.be.equal(false); - expect(result.current.showAutoTranslate({ u: { _id: 2 }, translations: { lang: 'translated' } })).to.be.equal(false); - }); -}); diff --git a/apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts b/apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts deleted file mode 100644 index 3ec8fbec36d8..000000000000 --- a/apps/meteor/client/views/room/components/body/composer/MessageComposer.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Anonymous read enabled: true -// Anonymous write enabled: true - -// Room public: joinCodeRequired: true -// Room public: joinCodeRequired: false - -// federation: enabled - -// read only: true and has no permission to post -// read only: true but has permission to post - -// omnichannel: onhold -// omnichannel: custom composers -// omnichannel: inquiry diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index 9232c2065dad..af14e7240e09 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -3,12 +3,7 @@ export default { testEnvironment: 'jsdom', modulePathIgnorePatterns: ['<rootDir>/dist/'], - testMatch: [ - '<rootDir>/client/hooks/**.spec.[jt]s?(x)', - '<rootDir>/client/components/**.spec.[jt]s?(x)', - '<rootDir>client/components/message/content/reactions/**.spec.[jt]s?(x)', - '<rootDir>/client/sidebar/header/actions/hooks/**/**.spec.[jt]s?(x)', - ], + testMatch: ['<rootDir>/client/**/**.spec.[jt]s?(x)'], transform: { '^.+\\.(t|j)sx?$': '@swc/jest', }, diff --git a/ee/packages/api-client/jest.config.ts b/ee/packages/api-client/jest.config.ts index 2e2575dc1209..455fa3a054f2 100644 --- a/ee/packages/api-client/jest.config.ts +++ b/ee/packages/api-client/jest.config.ts @@ -9,4 +9,5 @@ export default { moduleNameMapper: { '\\.css$': 'identity-obj-proxy', }, + collectCoverage: true, }; From 174c28d40b3d5a52023ee2dca2e81dd77ff33fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Wed, 19 Jul 2023 18:01:19 -0300 Subject: [PATCH 131/149] chore: bump fuselage (#29872) --- .../HeaderToolbox/HeaderToolboxAction.tsx | 2 +- yarn.lock | 35 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx b/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx index d8d97e063ba8..48db84657959 100644 --- a/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx +++ b/packages/ui-client/src/components/Header/HeaderToolbox/HeaderToolboxAction.tsx @@ -13,7 +13,7 @@ const HeaderToolboxAction = forwardRef<HTMLButtonElement, any>(function HeaderTo data-toolbox={index} key={id} icon={icon} - tiny + small position='relative' overflow='visible' {...(tooltip ? { 'data-tooltip': tooltip, 'title': '' } : { title })} diff --git a/yarn.lock b/yarn.lock index ecde951e5d8b..23ec581c7f14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9457,7 +9457,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.160": +"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.160, @rocket.chat/css-in-js@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9483,7 +9483,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.160": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.160, @rocket.chat/css-supports@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9692,13 +9692,20 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:next, @rocket.chat/fuselage-tokens@npm:~0.32.0-dev.336": +"@rocket.chat/fuselage-tokens@npm:next": version: 0.32.0-dev.336 resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.336" checksum: 2cf52211b3a3ee9a6111db46bc05918244190be7a6fef482011dccc0cc9a0777362dd91010e9df45c67b2c3bcaf00e35a7fbdddfdb5e7f683a19baa1fa8211d0 languageName: node linkType: hard +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.341": + version: 0.32.0-dev.341 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.341" + checksum: f8944cbae4ea3a90c4127627e4f90ab1becc916c72ec54e1f16fd8de3e2bb8c0c6ca1dc1aa87ae7d2dc7d105f325dbbe65c262eca5719f6d237e7b2aa35a321e + 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" @@ -9755,14 +9762,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.386 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.386" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.160 - "@rocket.chat/css-supports": ~0.31.23-dev.160 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.336 - "@rocket.chat/memo": ~0.31.23-dev.160 - "@rocket.chat/styled": ~0.31.23-dev.160 + version: 0.32.0-dev.391 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.391" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.165 + "@rocket.chat/css-supports": ~0.31.23-dev.165 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.341 + "@rocket.chat/memo": ~0.31.23-dev.165 + "@rocket.chat/styled": ~0.31.23-dev.165 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9774,7 +9781,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 3753fe5939b7fdead82076b824959bc7ed537349f810c104432d2ab1f7aa166a374642664e82e2cdc9efa3d1b4e1807c5ed12777cf7e875de24fca4f0ed58149 + checksum: 2d32285d0354bbadb876a01ad04437b28a44ef22ed76468972a99923c976e32859a9ce66f83cbf650db15e8737bbe3f4938ac6975fed8d8462f9329be37d3c0e languageName: node linkType: hard @@ -9998,7 +10005,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.160": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.160, @rocket.chat/memo@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -10824,7 +10831,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.160": +"@rocket.chat/styled@npm:~0.31.23-dev.160, @rocket.chat/styled@npm:~0.31.23-dev.165": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: From 1c27dc1e9beb10a15c658558c79376339be93fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlia=20Jaeger=20Foresti?= <60678893+juliajforesti@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:40:29 -0300 Subject: [PATCH 132/149] chore: sidebar actions pressed state (#29877) --- apps/meteor/client/sidebar/header/actions/Directory.tsx | 9 ++++++++- apps/meteor/client/sidebar/header/actions/Home.tsx | 4 +++- ee/packages/ui-theming/src/sidebarPalette.ts | 2 +- ee/packages/ui-theming/src/sidebarPaletteDark.ts | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/meteor/client/sidebar/header/actions/Directory.tsx b/apps/meteor/client/sidebar/header/actions/Directory.tsx index cdfdcfc1eaf1..f5caacf069a3 100644 --- a/apps/meteor/client/sidebar/header/actions/Directory.tsx +++ b/apps/meteor/client/sidebar/header/actions/Directory.tsx @@ -14,7 +14,14 @@ const Directory = (props: DirectoryProps) => { router.navigate('/directory'); }); - return <Sidebar.TopBar.Action {...props} icon='notebook-hashtag' onClick={handleDirectory} />; + return ( + <Sidebar.TopBar.Action + {...props} + icon='notebook-hashtag' + onClick={handleDirectory} + pressed={router.getLocationPathname().includes('/directory')} + /> + ); }; export default Directory; diff --git a/apps/meteor/client/sidebar/header/actions/Home.tsx b/apps/meteor/client/sidebar/header/actions/Home.tsx index 2c4bbec40f51..0a67a9dbca26 100644 --- a/apps/meteor/client/sidebar/header/actions/Home.tsx +++ b/apps/meteor/client/sidebar/header/actions/Home.tsx @@ -13,7 +13,9 @@ const SidebarHeaderActionHome: VFC<Omit<HTMLAttributes<HTMLElement>, 'is'>> = (p router.navigate('/home'); }); - return showHome ? <Sidebar.TopBar.Action {...props} icon='home' onClick={handleHome} /> : null; + return showHome ? ( + <Sidebar.TopBar.Action {...props} icon='home' onClick={handleHome} pressed={router.getLocationPathname().includes('/home')} /> + ) : null; }; export default SidebarHeaderActionHome; diff --git a/ee/packages/ui-theming/src/sidebarPalette.ts b/ee/packages/ui-theming/src/sidebarPalette.ts index c25ad6272db8..9512af1e467c 100644 --- a/ee/packages/ui-theming/src/sidebarPalette.ts +++ b/ee/packages/ui-theming/src/sidebarPalette.ts @@ -53,7 +53,7 @@ export const palette = [ list: [ { name: 'button-background-secondary-default', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-hover', token: '', color: '#3A404B' }, - { name: 'button-background-secondary-press', token: '', color: '#2C313A' }, + { name: 'button-background-secondary-press', token: '', color: '#4C5362' }, { name: 'button-background-secondary-focus', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-keyfocus', token: '', color: '#2F343D' }, { name: 'button-background-secondary-disabled', token: '', color: '#2F343D' }, diff --git a/ee/packages/ui-theming/src/sidebarPaletteDark.ts b/ee/packages/ui-theming/src/sidebarPaletteDark.ts index b2e7cc4e9831..2b7801f118fb 100644 --- a/ee/packages/ui-theming/src/sidebarPaletteDark.ts +++ b/ee/packages/ui-theming/src/sidebarPaletteDark.ts @@ -55,7 +55,7 @@ export const palette = [ list: [ { name: 'button-background-secondary-default', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-hover', token: '', color: '#3A404B' }, - { name: 'button-background-secondary-press', token: '', color: '#2C313A' }, + { name: 'button-background-secondary-press', token: '', color: '#4C5362' }, { name: 'button-background-secondary-focus', token: '', color: '#0D0F11' }, { name: 'button-background-secondary-keyfocus', token: '', color: '#2F343D' }, { name: 'button-background-secondary-disabled', token: '', color: '#2F343D' }, From 54ef89c9a7a065747f1172b767e0da30743df164 Mon Sep 17 00:00:00 2001 From: csuadev <72958726+csuadev@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:11:01 -0500 Subject: [PATCH 133/149] fix: show requested filters only on requested apps view (#29819) Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com> --- .changeset/silver-mugs-unite.md | 5 +++ .../marketplace/AppsPage/AppsPageContent.tsx | 39 ++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 .changeset/silver-mugs-unite.md diff --git a/.changeset/silver-mugs-unite.md b/.changeset/silver-mugs-unite.md new file mode 100644 index 000000000000..be74b1bef215 --- /dev/null +++ b/.changeset/silver-mugs-unite.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +fix: show requested filters only on requested apps view diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx index e9b91b663d8d..b652aba5ca27 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageContent.tsx @@ -55,17 +55,36 @@ const AppsPageContent = (): ReactElement => { }); const statusFilterOnSelected = useRadioToggle(setStatusFilterStructure); - const [sortFilterStructure, setSortFilterStructure] = useState<RadioDropDownGroup>({ - label: t('Sort_By'), - items: [ - { id: 'urf', label: t('Unread_Requested_First'), checked: false }, - { id: 'url', label: t('Unread_Requested_Last'), checked: false }, - { id: 'az', label: 'A-Z', checked: false }, - { id: 'za', label: 'Z-A', checked: false }, - { id: 'mru', label: t('Most_recent_updated'), checked: true }, - { id: 'lru', label: t('Least_recent_updated'), checked: false }, - ], + const baseFilterStructureItems = [ + { id: 'az', label: 'A-Z', checked: false }, + { id: 'za', label: 'Z-A', checked: false }, + { id: 'mru', label: t('Most_recent_updated'), checked: true }, + { id: 'lru', label: t('Least_recent_updated'), checked: false }, + ]; + + const requestedFilterItems = [ + { id: 'urf', label: t('Unread_Requested_First'), checked: false }, + { id: 'url', label: t('Unread_Requested_Last'), checked: false }, + ]; + + const createFilterStructureItems = () => { + return isRequested ? [...requestedFilterItems, ...baseFilterStructureItems] : baseFilterStructureItems; + }; + + const [sortFilterStructure, setSortFilterStructure] = useState<RadioDropDownGroup>(() => { + return { + label: t('Sort_By'), + items: createFilterStructureItems(), + }; }); + + useEffect(() => { + setSortFilterStructure({ + label: t('Sort_By'), + items: createFilterStructureItems(), + }); + }, [isRequested]); + const sortFilterOnSelected = useRadioToggle(setSortFilterStructure); const getAppsData = useCallback((): appsDataType => { From 2047ab23d59b5219d18028591c067aa24b2f8f32 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi <marcos.defendi@rocket.chat> Date: Thu, 20 Jul 2023 16:11:33 -0300 Subject: [PATCH 134/149] chore: move some message functions to message service (#29793) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../server/services/messages/service.ts | 22 ++++++++++++++++++- .../src/types/IMessageService.ts | 6 ++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/meteor/server/services/messages/service.ts b/apps/meteor/server/services/messages/service.ts index 2f15cdbadb51..e5d30130e331 100644 --- a/apps/meteor/server/services/messages/service.ts +++ b/apps/meteor/server/services/messages/service.ts @@ -1,10 +1,14 @@ -import type { IMessage, MessageTypesValues, IUser } from '@rocket.chat/core-typings'; +import type { IMessage, MessageTypesValues, IUser, IRoom } from '@rocket.chat/core-typings'; import type { IMessageService } from '@rocket.chat/core-services'; import { ServiceClassInternal } from '@rocket.chat/core-services'; import { Messages } from '@rocket.chat/models'; import { executeSendMessage } from '../../../app/lib/server/methods/sendMessage'; import { settings } from '../../../app/settings/server'; +import { sendMessage } from '../../../app/lib/server/functions/sendMessage'; +import { deleteMessage } from '../../../app/lib/server/functions/deleteMessage'; +import { updateMessage } from '../../../app/lib/server/functions/updateMessage'; +import { executeSetReaction } from '../../../app/reactions/server/setReaction'; export class MessageService extends ServiceClassInternal implements IMessageService { protected name = 'message'; @@ -13,6 +17,22 @@ export class MessageService extends ServiceClassInternal implements IMessageServ return executeSendMessage(fromId, { rid, msg }); } + async sendMessageWithValidation(user: IUser, message: Partial<IMessage>, room: Partial<IRoom>, upsert = false): Promise<IMessage> { + return sendMessage(user, message, room, upsert); + } + + async deleteMessage(user: IUser, message: IMessage): Promise<void> { + return deleteMessage(message, user); + } + + async updateMessage(message: IMessage, user: IUser, originalMsg?: IMessage): Promise<void> { + return updateMessage(message, user, originalMsg); + } + + async reactToMessage(userId: string, reaction: string, messageId: IMessage['_id'], shouldReact?: boolean): Promise<void> { + return executeSetReaction(userId, reaction, messageId, shouldReact); + } + async saveSystemMessage<T = IMessage>( type: MessageTypesValues, rid: string, diff --git a/packages/core-services/src/types/IMessageService.ts b/packages/core-services/src/types/IMessageService.ts index cf02b9e664be..ea8f207df67d 100644 --- a/packages/core-services/src/types/IMessageService.ts +++ b/packages/core-services/src/types/IMessageService.ts @@ -1,4 +1,4 @@ -import type { IMessage, MessageTypesValues, IUser } from '@rocket.chat/core-typings'; +import type { IMessage, MessageTypesValues, IUser, IRoom } from '@rocket.chat/core-typings'; export interface IMessageService { sendMessage({ fromId, rid, msg }: { fromId: string; rid: string; msg: string }): Promise<IMessage>; @@ -9,4 +9,8 @@ export interface IMessageService { user: Pick<IUser, '_id' | 'username' | 'name'>, extraData?: Partial<T>, ): Promise<IMessage['_id']>; + sendMessageWithValidation(user: IUser, message: Partial<IMessage>, room: Partial<IRoom>, upsert?: boolean): Promise<IMessage>; + deleteMessage(user: IUser, message: IMessage): Promise<void>; + updateMessage(message: IMessage, user: IUser, originalMsg?: IMessage): Promise<void>; + reactToMessage(userId: string, reaction: string, messageId: IMessage['_id'], shouldReact?: boolean): Promise<void>; } From 92b690d206b02e8b80276173a1d8ad940359d385 Mon Sep 17 00:00:00 2001 From: Disha Bhardwaj <67470541+bhardwajdisha@users.noreply.github.com> Date: Fri, 21 Jul 2023 01:56:29 +0530 Subject: [PATCH 135/149] fix: Wrong toast message while creating a new custom sound with an existing name (#28301) Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com> --- .changeset/empty-ants-enjoy.md | 5 +++++ .../client/views/admin/customSounds/AddCustomSound.tsx | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/empty-ants-enjoy.md diff --git a/.changeset/empty-ants-enjoy.md b/.changeset/empty-ants-enjoy.md new file mode 100644 index 000000000000..4a55f82d0abf --- /dev/null +++ b/.changeset/empty-ants-enjoy.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': minor +--- + +fix: Wrong toast message while creating a new custom sound with an existing name diff --git a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx index 35feae9113cc..e69186ed5521 100644 --- a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx @@ -72,7 +72,9 @@ const AddCustomSound = ({ goToNew, close, onChange, ...props }: AddCustomSoundPr const handleSave = useCallback(async () => { try { const result = await saveAction(name, sound); - dispatchToastMessage({ type: 'success', message: t('Custom_Sound_Saved_Successfully') }); + if (result) { + dispatchToastMessage({ type: 'success', message: t('Custom_Sound_Saved_Successfully') }); + } result && goToNew(result); onChange(); } catch (error) { From e6b9a72da55299ecafd3b4657abad3dd5b4e3b68 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi <marcos.defendi@rocket.chat> Date: Thu, 20 Jul 2023 18:16:33 -0300 Subject: [PATCH 136/149] chore: move some room functions to room service (#29794) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- .../app/lib/server/functions/createRoom.ts | 4 +- .../server/functions/removeUserFromRoom.ts | 2 +- apps/meteor/server/services/room/service.ts | 52 +++++++++++++++++-- .../core-services/src/types/IRoomService.ts | 25 ++++++++- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 6d0048659501..836cb8917cbc 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -5,10 +5,10 @@ import { Message, Team } from '@rocket.chat/core-services'; import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services'; import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; -import { Apps } from '../../../../ee/server/apps'; +import { Apps } from '../../../../ee/server/apps/orchestrator'; import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; import { callbacks } from '../../../../lib/callbacks'; -import { getValidRoomName } from '../../../utils/server'; +import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; import { createDirectRoom } from './createDirectRoom'; const isValidName = (name: unknown): name is string => { diff --git a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts index bca939bc520b..91c20ab6715c 100644 --- a/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts +++ b/apps/meteor/app/lib/server/functions/removeUserFromRoom.ts @@ -5,7 +5,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Message, Team } from '@rocket.chat/core-services'; import { Subscriptions, Rooms } from '@rocket.chat/models'; -import { AppEvents, Apps } from '../../../../ee/server/apps'; +import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; export const removeUserFromRoom = async function ( diff --git a/apps/meteor/server/services/room/service.ts b/apps/meteor/server/services/room/service.ts index 88a94edee987..d34e5bd84dde 100644 --- a/apps/meteor/server/services/room/service.ts +++ b/apps/meteor/server/services/room/service.ts @@ -1,10 +1,15 @@ -import type { IRoom, IUser } from '@rocket.chat/core-typings'; +import type { AtLeast, IRoom, IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import { ServiceClassInternal, Authorization } from '@rocket.chat/core-services'; import type { ICreateRoomParams, IRoomService } from '@rocket.chat/core-services'; import { createRoom } from '../../../app/lib/server/functions/createRoom'; // TODO remove this import import { createDirectMessage } from '../../methods/createDirectMessage'; +import { addUserToRoom } from '../../../app/lib/server/functions/addUserToRoom'; +import { removeUserFromRoom } from '../../../app/lib/server/functions/removeUserFromRoom'; +import { getValidRoomName } from '../../../app/utils/server/lib/getValidRoomName'; +import { saveRoomTopic } from '../../../app/channel-settings/server/functions/saveRoomTopic'; +import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; export class RoomService extends ServiceClassInternal implements IRoomService { protected name = 'room'; @@ -34,10 +39,14 @@ export class RoomService extends ServiceClassInternal implements IRoomService { Users.findOneById(from, { projection: { _id: 1 } }), ]); - if (!toUser || !fromUser) { + if (!toUser?.username || !fromUser) { throw new Error('error-invalid-user'); } - return createDirectMessage([toUser.username], fromUser._id); + return this.createDirectMessageWithMultipleUsers([toUser.username], fromUser._id); + } + + async createDirectMessageWithMultipleUsers(members: string[], creatorId: string): Promise<{ rid: string }> { + return createDirectMessage(members, creatorId); } async addMember(uid: string, rid: string): Promise<boolean> { @@ -48,4 +57,41 @@ export class RoomService extends ServiceClassInternal implements IRoomService { return true; } + + async addUserToRoom( + roomId: string, + user: Pick<IUser, '_id' | 'username'> | string, + inviter?: Pick<IUser, '_id' | 'username'>, + silenced?: boolean, + ): Promise<boolean | undefined> { + return addUserToRoom(roomId, user, inviter, silenced); + } + + async removeUserFromRoom(roomId: string, user: IUser, options?: { byUser: Pick<IUser, '_id' | 'username'> }): Promise<void> { + return removeUserFromRoom(roomId, user, options); + } + + async getValidRoomName( + displayName: string, + roomId = '', + options: { allowDuplicates?: boolean; nameValidationRegex?: string } = {}, + ): Promise<string> { + return getValidRoomName(displayName, roomId, options); + } + + async saveRoomTopic( + roomId: string, + roomTopic: string | undefined, + user: { + username: string; + _id: string; + }, + sendMessage = true, + ): Promise<void> { + await saveRoomTopic(roomId, roomTopic, user, sendMessage); + } + + async getRouteLink(room: AtLeast<IRoom, '_id' | 't' | 'name'>): Promise<string | boolean> { + return roomCoordinator.getRouteLink(room.t as string, { rid: room._id, name: room.name }); + } } diff --git a/packages/core-services/src/types/IRoomService.ts b/packages/core-services/src/types/IRoomService.ts index b8f4ccde0ec2..e69707e18a36 100644 --- a/packages/core-services/src/types/IRoomService.ts +++ b/packages/core-services/src/types/IRoomService.ts @@ -1,4 +1,4 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { AtLeast, IRoom, IUser } from '@rocket.chat/core-typings'; export interface ISubscriptionExtraData { open: boolean; @@ -29,4 +29,27 @@ export interface IRoomService { addMember(uid: string, rid: string): Promise<boolean>; create(uid: string, params: ICreateRoomParams): Promise<IRoom>; createDirectMessage(data: { to: string; from: string }): Promise<{ rid: string }>; + createDirectMessageWithMultipleUsers(members: string[], creatorId: string): Promise<{ rid: string }>; + addUserToRoom( + roomId: string, + user: Pick<IUser, '_id' | 'username'> | string, + inviter?: Pick<IUser, '_id' | 'username'>, + silenced?: boolean, + ): Promise<boolean | undefined>; + removeUserFromRoom(roomId: string, user: IUser, options?: { byUser: Pick<IUser, '_id' | 'username'> }): Promise<void>; + getValidRoomName( + displayName: string, + roomId?: string, + options?: { allowDuplicates?: boolean; nameValidationRegex?: string }, + ): Promise<string>; + saveRoomTopic( + roomId: string, + roomTopic: string | undefined, + user: { + username: string; + _id: string; + }, + sendMessage?: boolean, + ): Promise<void>; + getRouteLink(room: AtLeast<IRoom, '_id' | 't' | 'name'>): Promise<string | boolean>; } From e9a42ba779392e3d3975449d4662210c74fc764b Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Thu, 20 Jul 2023 19:17:20 -0300 Subject: [PATCH 137/149] chore: Reduce complexity to infer IconName type (#29881) --- .../client/messageBox/messageBoxFormatting.ts | 5 ++--- apps/meteor/app/ui-utils/client/lib/AccountBox.ts | 5 ++--- apps/meteor/app/ui-utils/client/lib/MessageAction.ts | 6 +++--- .../client/components/GenericModal/GenericModal.tsx | 6 +++--- .../components/GenericNoResults/GenericNoResults.tsx | 5 ++--- .../client/components/InfoPanel/InfoPanelAction.tsx | 3 ++- .../client/components/InfoPanel/InfoPanelTitle.tsx | 5 +++-- .../components/Sidebar/SidebarNavigationItem.tsx | 4 ++-- apps/meteor/client/components/UpsellModal.tsx | 4 ++-- .../client/components/UserInfo/UserInfoAction.tsx | 3 ++- .../components/message/content/MessageActions.tsx | 4 ++-- .../message/content/actions/MessageAction.tsx | 8 ++++---- apps/meteor/client/lib/banners.ts | 5 ++--- apps/meteor/client/lib/createSidebarItems.ts | 4 ++-- apps/meteor/client/sidebar/Item/Condensed.tsx | 4 ++-- apps/meteor/client/sidebar/Item/Extended.tsx | 4 ++-- apps/meteor/client/views/banners/UiKitBanner.tsx | 5 +++-- apps/meteor/client/views/hooks/useActionSpread.ts | 6 +++--- .../views/omnichannel/agents/AgentInfoAction.tsx | 4 ++-- .../room/components/contextualBar/MessageListTab.tsx | 6 +++--- .../components/NotificationByDevice.tsx | 5 +++-- .../room/contextualBar/OTR/components/OTRStates.tsx | 6 +++--- apps/meteor/client/views/room/lib/Toolbox/index.tsx | 5 +++-- .../WebdavFilePickerGrid/WebdavFilePickerGrid.tsx | 4 ++-- .../WebdavFilePickerModal/WebdavFilePickerTable.tsx | 4 ++-- .../WebdavFilePickerModal/lib/getNodeIconType.ts | 10 ++++++++-- .../views/teams/contextualBar/info/TeamsInfo.tsx | 5 ++--- apps/meteor/definition/IRoomTypeConfig.ts | 5 ++--- .../client/deviceManagement/components/DeviceIcon.tsx | 5 +++-- .../ui-client/src/components/Header/HeaderIcon.tsx | 5 +++-- packages/ui-video-conf/src/VideoConfButton.tsx | 5 +++-- packages/ui-video-conf/src/VideoConfController.tsx | 4 ++-- .../web-ui-registration/src/LoginServicesButton.tsx | 5 +++-- 33 files changed, 87 insertions(+), 77 deletions(-) diff --git a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts index bc3fb8a011cc..bd8a9e149cca 100644 --- a/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts +++ b/apps/meteor/app/ui-message/client/messageBox/messageBoxFormatting.ts @@ -1,13 +1,12 @@ -import type { Icon } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { settings } from '../../../settings/client'; export type FormattingButton = | { label: TranslationKey; - icon: ComponentProps<typeof Icon>['name']; + icon: IconName; pattern: string; // text?: () => string | undefined; command?: string; diff --git a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts index c7bda9df1659..4bc39fdbaa68 100644 --- a/apps/meteor/app/ui-utils/client/lib/AccountBox.ts +++ b/apps/meteor/app/ui-utils/client/lib/AccountBox.ts @@ -1,8 +1,7 @@ import type { IUIActionButton, IUActionButtonWhen } from '@rocket.chat/apps-engine/definition/ui/IUIActionButtonDescriptor'; import type { UserStatus } from '@rocket.chat/core-typings'; import type { TranslationKey, LocationPathname } from '@rocket.chat/ui-contexts'; -import type { Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { sdk } from '../../../utils/client/lib/SDKClient'; @@ -18,7 +17,7 @@ export interface IAppAccountBoxItem extends IUIActionButton { export type AccountBoxItem = { name: TranslationKey; - icon: ComponentProps<typeof Icon>['name']; + icon: IconName; href: LocationPathname; sideNav?: string; condition: () => boolean; diff --git a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts index 6fd101864bc8..d909bda53eaa 100644 --- a/apps/meteor/app/ui-utils/client/lib/MessageAction.ts +++ b/apps/meteor/app/ui-utils/client/lib/MessageAction.ts @@ -1,11 +1,11 @@ -import type { ComponentProps, ContextType } from 'react'; +import type { ContextType } from 'react'; import mem from 'mem'; import { Meteor } from 'meteor/meteor'; import { ReactiveVar } from 'meteor/reactive-var'; import { Tracker } from 'meteor/tracker'; -import type { Icon } from '@rocket.chat/fuselage'; import type { IMessage, IUser, ISubscription, IRoom, SettingValue, Serialized, ITranslatedMessage } from '@rocket.chat/core-typings'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { Messages, ChatRoom, Subscriptions } from '../../../models/client'; import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator'; @@ -48,7 +48,7 @@ type MessageActionConditionProps = { export type MessageActionConfig = { id: string; - icon: ComponentProps<typeof Icon>['name']; + icon: IconName; variant?: 'danger' | 'success' | 'warning'; label: TranslationKey; order?: number; diff --git a/apps/meteor/client/components/GenericModal/GenericModal.tsx b/apps/meteor/client/components/GenericModal/GenericModal.tsx index 09a9598dafc2..bf5d808f5b5b 100644 --- a/apps/meteor/client/components/GenericModal/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal/GenericModal.tsx @@ -1,5 +1,5 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { Button, Modal } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { FC, ComponentProps, ReactElement, ReactNode } from 'react'; import React from 'react'; @@ -15,14 +15,14 @@ type GenericModalProps = RequiredModalProps & { cancelText?: ReactNode; confirmText?: ReactNode; title?: string | ReactElement; - icon?: ComponentProps<typeof Icon>['name'] | ReactElement | null; + icon?: IconName | ReactElement | null; confirmDisabled?: boolean; tagline?: ReactNode; onCancel?: () => Promise<void> | void; onClose?: () => Promise<void> | void; } & Omit<ComponentProps<typeof Modal>, 'title'>; -const iconMap: Record<string, ComponentProps<typeof Icon>['name']> = { +const iconMap: Record<string, IconName> = { danger: 'modal-warning', warning: 'modal-warning', info: 'info', diff --git a/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx b/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx index f9d0803a98d6..5a630898ab13 100644 --- a/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx +++ b/apps/meteor/client/components/GenericNoResults/GenericNoResults.tsx @@ -1,11 +1,10 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ComponentProps } from 'react'; import React from 'react'; type GenericNoResultsProps = { - icon?: ComponentProps<typeof Icon>['name']; + icon?: IconName; title?: string; description?: string; buttonTitle?: string; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx index d185c6ac5e68..17b555e0b820 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx @@ -1,9 +1,10 @@ import { Icon, Button } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ComponentProps, ReactElement, ReactNode } from 'react'; import React from 'react'; type InfoPanelActionProps = Omit<ComponentProps<typeof Button>, 'label'> & { - icon?: ComponentProps<typeof Icon>['name']; + icon?: IconName; label: ReactNode; }; diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx index 298a01bd2045..2714b5fec0e3 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx @@ -1,5 +1,6 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, FC, ReactNode } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { FC, ReactNode } from 'react'; import React from 'react'; type InfoPanelTitleProps = { @@ -7,7 +8,7 @@ type InfoPanelTitleProps = { icon: ReactNode; }; -const isValidIcon = (icon: ReactNode): icon is ComponentProps<typeof Icon>['name'] => typeof icon === 'string'; +const isValidIcon = (icon: ReactNode): icon is IconName => typeof icon === 'string'; const InfoPanelTitle: FC<InfoPanelTitleProps> = ({ title, icon }) => ( <Box display='flex' flexShrink={0} alignItems='center' fontScale='h4' color='default' withTruncatedText> diff --git a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx index ed6cd3e91d3b..4bfacbedbdfc 100644 --- a/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx +++ b/apps/meteor/client/components/Sidebar/SidebarNavigationItem.tsx @@ -1,5 +1,5 @@ import { Box, Icon, Tag } from '@rocket.chat/fuselage'; -import type { IconProps } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { FC, ReactElement } from 'react'; import React, { memo } from 'react'; @@ -8,7 +8,7 @@ import SidebarGenericItem from './SidebarGenericItem'; type SidebarNavigationItemProps = { permissionGranted?: (() => boolean) | boolean; pathSection: string; - icon?: IconProps['name']; + icon?: IconName; label?: string; tag?: string; currentPath?: string; diff --git a/apps/meteor/client/components/UpsellModal.tsx b/apps/meteor/client/components/UpsellModal.tsx index 08cb1171e9ee..ad34db316784 100644 --- a/apps/meteor/client/components/UpsellModal.tsx +++ b/apps/meteor/client/components/UpsellModal.tsx @@ -1,5 +1,5 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { Box, Button, Modal } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactNode, ReactElement, ComponentProps } from 'react'; import React from 'react'; @@ -12,7 +12,7 @@ type UpsellModalProps = { title: string | ReactElement; subtitle?: string | ReactElement; description?: string | ReactElement; - icon?: ComponentProps<typeof Icon>['name']; + icon?: IconName; img: ComponentProps<typeof Modal.HeroImage>['src']; onCancel?: () => void; onClose?: () => void; diff --git a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx index 958606e3570e..9ed7bb5c527a 100644 --- a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx @@ -1,9 +1,10 @@ import { Button, Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactElement, ComponentProps } from 'react'; import React from 'react'; type UserInfoActionProps = { - icon: ComponentProps<typeof Icon>['name']; + icon: IconName; } & ComponentProps<typeof Button>; const UserInfoAction = ({ icon, label, ...props }: UserInfoActionProps): ReactElement => ( diff --git a/apps/meteor/client/components/message/content/MessageActions.tsx b/apps/meteor/client/components/message/content/MessageActions.tsx index 2ee6c110f6ac..d38fbb6c64df 100644 --- a/apps/meteor/client/components/message/content/MessageActions.tsx +++ b/apps/meteor/client/components/message/content/MessageActions.tsx @@ -1,7 +1,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { IconProps } from '@rocket.chat/fuselage'; import { Box, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -10,7 +10,7 @@ import { actionLinks } from '../../../lib/actionLinks'; import MessageAction from './actions/MessageAction'; type MessageActionOptions = { - icon: IconProps['name']; + icon: IconName; i18nLabel?: TranslationKey; label?: string; methodId: string; diff --git a/apps/meteor/client/components/message/content/actions/MessageAction.tsx b/apps/meteor/client/components/message/content/actions/MessageAction.tsx index 9bd565f40fc8..a98e605e6129 100644 --- a/apps/meteor/client/components/message/content/actions/MessageAction.tsx +++ b/apps/meteor/client/components/message/content/actions/MessageAction.tsx @@ -1,20 +1,20 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { Icon, Button } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; -const resolveLegacyIcon = (legacyIcon: IconProps['name'] | `icon-${IconProps['name'] | 'videocam'}`): IconProps['name'] => { +const resolveLegacyIcon = (legacyIcon: IconName | `icon-${IconName | 'videocam'}`): IconName => { if (legacyIcon === 'icon-videocam') { return 'video'; } - return legacyIcon?.replace(/^icon-/, '') as IconProps['name']; + return legacyIcon?.replace(/^icon-/, '') as IconName; }; type MessageActionProps = { - icon: IconProps['name']; + icon: IconName; i18nLabel?: TranslationKey; label?: string; methodId: string; diff --git a/apps/meteor/client/lib/banners.ts b/apps/meteor/client/lib/banners.ts index dfbc9ad462ec..89310da2e3c7 100644 --- a/apps/meteor/client/lib/banners.ts +++ b/apps/meteor/client/lib/banners.ts @@ -1,7 +1,6 @@ import type { UiKitBannerPayload } from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; -import type { Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; export type LegacyBannerPayload = { id: string; @@ -9,7 +8,7 @@ export type LegacyBannerPayload = { title?: string | (() => string); text?: string | (() => string); html?: string | (() => string); - icon?: ComponentProps<typeof Icon>['name']; + icon?: IconName; modifiers?: ('large' | 'danger')[]; timer?: number; action?: () => Promise<void> | void; diff --git a/apps/meteor/client/lib/createSidebarItems.ts b/apps/meteor/client/lib/createSidebarItems.ts index 8d836cc293ce..3e8485416267 100644 --- a/apps/meteor/client/lib/createSidebarItems.ts +++ b/apps/meteor/client/lib/createSidebarItems.ts @@ -1,11 +1,11 @@ -import type { IconProps } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { LocationPathname } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; export type Item = { i18nLabel: string; href?: LocationPathname | `https://go.rocket.chat/i/${string}`; - icon?: IconProps['name']; + icon?: IconName; tag?: 'Alpha' | 'Beta'; permissionGranted?: () => boolean; pathSection?: string; diff --git a/apps/meteor/client/sidebar/Item/Condensed.tsx b/apps/meteor/client/sidebar/Item/Condensed.tsx index 527d38a0879c..e964958f635f 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.tsx @@ -1,6 +1,6 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { IconButton, Sidebar } from '@rocket.chat/fuselage'; import { useMutableCallback, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { FC, ReactElement } from 'react'; import React, { memo, useState } from 'react'; @@ -8,7 +8,7 @@ type CondensedProps = { title: ReactElement | string; titleIcon?: ReactElement; avatar: ReactElement | boolean; - icon?: IconProps['name']; + icon?: IconName; actions?: ReactElement; href?: string; unread?: boolean; diff --git a/apps/meteor/client/sidebar/Item/Extended.tsx b/apps/meteor/client/sidebar/Item/Extended.tsx index c896037d7f3d..c997a48b5b4a 100644 --- a/apps/meteor/client/sidebar/Item/Extended.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.tsx @@ -1,13 +1,13 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { Sidebar, IconButton } from '@rocket.chat/fuselage'; import { useMutableCallback, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { VFC } from 'react'; import React, { memo, useState } from 'react'; import { useShortTimeAgo } from '../../hooks/useTimeAgo'; type ExtendedProps = { - icon?: IconProps['name']; + icon?: IconName; title?: React.ReactNode; avatar?: React.ReactNode | boolean; actions?: React.ReactNode; diff --git a/apps/meteor/client/views/banners/UiKitBanner.tsx b/apps/meteor/client/views/banners/UiKitBanner.tsx index d7f55f370e48..1ea0b6bf0727 100644 --- a/apps/meteor/client/views/banners/UiKitBanner.tsx +++ b/apps/meteor/client/views/banners/UiKitBanner.tsx @@ -1,8 +1,9 @@ import type { UIKitActionEvent, UiKitBannerProps } from '@rocket.chat/core-typings'; import { Banner, Icon } from '@rocket.chat/fuselage'; import { kitContext, bannerParser, UiKitBanner as renderUiKitBannerBlocks } from '@rocket.chat/fuselage-ui-kit'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { LayoutBlock } from '@rocket.chat/ui-kit'; -import type { FC, ComponentProps, ReactElement, ContextType } from 'react'; +import type { FC, ReactElement, ContextType } from 'react'; import React, { useMemo } from 'react'; import { useUIKitHandleAction } from '../../UIKit/hooks/useUIKitHandleAction'; @@ -19,7 +20,7 @@ const UiKitBanner: FC<UiKitBannerProps> = ({ payload }) => { const icon = useMemo(() => { if (state.icon) { - return <Icon name={state.icon as ComponentProps<typeof Icon>['name']} size={20} />; + return <Icon name={state.icon as IconName} size={20} />; } return null; diff --git a/apps/meteor/client/views/hooks/useActionSpread.ts b/apps/meteor/client/views/hooks/useActionSpread.ts index 513956800ec9..8cf69f1b62a3 100644 --- a/apps/meteor/client/views/hooks/useActionSpread.ts +++ b/apps/meteor/client/views/hooks/useActionSpread.ts @@ -1,10 +1,10 @@ -import type { Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactNode } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { ReactNode } from 'react'; import { useMemo } from 'react'; export type Action = { label: ReactNode; - icon?: ComponentProps<typeof Icon>['name']; + icon?: IconName; action: () => void; }; diff --git a/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx b/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx index 660cb6c3af14..9cb6e51170b7 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx @@ -1,10 +1,10 @@ -import type { IconProps } from '@rocket.chat/fuselage'; import { Button, Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { FC, HtmlHTMLAttributes } from 'react'; import React from 'react'; type AgentInfoActionProps = { - icon: IconProps['name']; + icon: IconName; label?: string; title?: string; } & Omit<HtmlHTMLAttributes<HTMLElement>, 'is'>; diff --git a/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx b/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx index eb4a7c8dbc60..e30dd3f3cd93 100644 --- a/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx +++ b/apps/meteor/client/views/room/components/contextualBar/MessageListTab.tsx @@ -1,9 +1,9 @@ import type { IMessage } from '@rocket.chat/core-typings'; -import type { Icon } from '@rocket.chat/fuselage'; import { Box, MessageDivider, Throbber } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; import type { UseQueryResult } from '@tanstack/react-query'; -import type { ReactElement, ComponentProps, ReactNode } from 'react'; +import type { ReactElement, ReactNode } from 'react'; import React, { useCallback } from 'react'; import { Virtuoso } from 'react-virtuoso'; @@ -29,7 +29,7 @@ import { useRoomSubscription } from '../../contexts/RoomContext'; import { useTabBarClose } from '../../contexts/ToolboxContext'; type MessageListTabProps = { - iconName: ComponentProps<typeof Icon>['name']; + iconName: IconName; title: ReactNode; emptyResultMessage: string; context: MessageActionContext; diff --git a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx index 85470d855902..2a6d3e9fbe39 100644 --- a/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx +++ b/apps/meteor/client/views/room/contextualBar/NotificationPreferences/components/NotificationByDevice.tsx @@ -1,10 +1,11 @@ import { Box, Accordion, Icon, FieldGroup } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactElement, ReactNode } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { ReactElement, ReactNode } from 'react'; import React, { memo } from 'react'; type NotificationByDeviceProps = { device: string; - icon: ComponentProps<typeof Icon>['name']; + icon: IconName; children: ReactNode; }; diff --git a/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx b/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx index e8db458ea326..0794c829b58a 100644 --- a/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx +++ b/apps/meteor/client/views/room/contextualBar/OTR/components/OTRStates.tsx @@ -1,13 +1,13 @@ -import type { Icon } from '@rocket.chat/fuselage'; import { States, StatesAction, StatesActions, StatesIcon, StatesSubtitle, StatesTitle } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; type OTRStatesProps = { title: string; description: string; - icon: ComponentProps<typeof Icon>['name']; + icon: IconName; onClickStart: () => void; }; diff --git a/apps/meteor/client/views/room/lib/Toolbox/index.tsx b/apps/meteor/client/views/room/lib/Toolbox/index.tsx index 6dde302f7e93..e4cecc3b95ca 100644 --- a/apps/meteor/client/views/room/lib/Toolbox/index.tsx +++ b/apps/meteor/client/views/room/lib/Toolbox/index.tsx @@ -1,5 +1,6 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import type { Box, Option, Icon } from '@rocket.chat/fuselage'; +import type { Box, Option } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import type { ReactNode, MouseEvent, ComponentProps, ComponentType } from 'react'; @@ -23,7 +24,7 @@ export type OptionRenderer = (props: OptionRendererProps) => ReactNode; export type ToolboxActionConfig = { 'id': string; - 'icon'?: ComponentProps<typeof Icon>['name']; + 'icon'?: IconName; 'title': TranslationKey; 'anonymous'?: boolean; 'data-tooltip'?: string; diff --git a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx index fda0e0da1107..5b6baf7c76a6 100644 --- a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx +++ b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerGrid/WebdavFilePickerGrid.tsx @@ -1,7 +1,7 @@ import type { IWebdavNode } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; import { Box, Icon, Skeleton, Palette } from '@rocket.chat/fuselage'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; import GenericNoResults from '../../../../../components/GenericNoResults'; @@ -38,7 +38,7 @@ const WebdavFilePickerGrid = ({ webdavNodes, onNodeClick, isLoading }: WebdavFil return ( <WebdavFilePickerGridItem key={index} className={hoverStyle} onClick={(): void => onNodeClick(webdavNode)}> - <Icon mie='x4' size='x72' name={icon as ComponentProps<typeof Icon>['name']} /> + <Icon mie='x4' size='x72' name={icon} /> <Box textAlign='center'>{webdavNode.basename}</Box> </WebdavFilePickerGridItem> ); diff --git a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx index 0345058448e8..e8fc95c45337 100644 --- a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx +++ b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/WebdavFilePickerTable.tsx @@ -1,7 +1,7 @@ import type { IWebdavNode } from '@rocket.chat/core-typings'; import { Box, Icon } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React from 'react'; import GenericNoResults from '../../../../components/GenericNoResults'; @@ -78,7 +78,7 @@ const WebdavFilePickerTable = ({ return ( <GenericTableRow key={index} onClick={(): void => onNodeClick(webdavNode)} tabIndex={index} role='link' action> <GenericTableCell fontScale='p2' color='default' w='x200' display='flex' alignItems='center'> - <Icon mie='x4' size='x20' name={icon as ComponentProps<typeof Icon>['name']} /> + <Icon mie='x4' size='x20' name={icon} /> <Box withTruncatedText>{webdavNode.basename}</Box> </GenericTableCell> <GenericTableCell fontScale='p2' color='default'> diff --git a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts index 5ecf53cfa77d..13c9e506dab0 100644 --- a/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts +++ b/apps/meteor/client/views/room/webdav/WebdavFilePickerModal/lib/getNodeIconType.ts @@ -1,5 +1,11 @@ -export const getNodeIconType = (basename: string, fileType: string, mime?: string): { icon: string; type: string; extension?: string } => { - let icon = 'clip'; +import type { Keys as IconName } from '@rocket.chat/icons'; + +export const getNodeIconType = ( + basename: string, + fileType: string, + mime?: string, +): { icon: IconName; type: string; extension?: string } => { + let icon: IconName = 'clip'; let type = ''; let extension = basename?.split('.').pop(); diff --git a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx index 501baf796f7d..16b75e8134f3 100644 --- a/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx +++ b/apps/meteor/client/views/teams/contextualBar/info/TeamsInfo.tsx @@ -1,8 +1,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; -import type { Icon } from '@rocket.chat/fuselage'; import { Box, Button, Callout, Option, Menu } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps } from 'react'; +import type { ReactElement } from 'react'; import React, { useMemo } from 'react'; import { @@ -118,7 +117,7 @@ const TeamsInfo = ({ const actions = useMemo(() => { const mapAction = ([key, { label, icon, action }]: [string, Action]): ReactElement => ( - <InfoPanel.Action key={key} label={label as string} onClick={action} icon={icon as ComponentProps<typeof Icon>['name']} /> + <InfoPanel.Action key={key} label={label as string} onClick={action} icon={icon} /> ); return [...actionsDefinition.map(mapAction), menu].filter(Boolean); diff --git a/apps/meteor/definition/IRoomTypeConfig.ts b/apps/meteor/definition/IRoomTypeConfig.ts index 1cf746ad1fe1..a5910aeb6e86 100644 --- a/apps/meteor/definition/IRoomTypeConfig.ts +++ b/apps/meteor/definition/IRoomTypeConfig.ts @@ -9,8 +9,7 @@ import type { ISubscription, IOmnichannelRoom, } from '@rocket.chat/core-typings'; -import type { ComponentProps } from 'react'; -import type { Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { IRouterPaths, RouteName } from '@rocket.chat/ui-contexts'; export type RoomIdentification = { rid?: IRoom['_id']; name?: string; tab?: string }; @@ -81,7 +80,7 @@ export interface IRoomTypeClientDirectives { getAvatarPath: ( room: AtLeast<IRoom, '_id' | 'name' | 'fname' | 'prid' | 'avatarETag' | 'uids' | 'usernames'> & { username?: IRoom['_id'] }, ) => string; - getIcon?: (room: Partial<IRoom>) => ComponentProps<typeof Icon>['name']; + getIcon?: (room: Partial<IRoom>) => IconName; extractOpenRoomParams?: (routeParams: Record<string, string | null | undefined>) => { type: RoomType; reference: string }; findRoom: (identifier: string) => IRoom | undefined; showJoinLink: (roomId: string) => boolean; diff --git a/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx b/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx index a1ae3f49891c..efc4a249c44f 100644 --- a/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx +++ b/apps/meteor/ee/client/deviceManagement/components/DeviceIcon.tsx @@ -1,8 +1,9 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactElement } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { ReactElement } from 'react'; import React from 'react'; -const iconMap: Record<string, ComponentProps<typeof Icon>['name']> = { +const iconMap: Record<string, IconName> = { 'browser': 'desktop', 'mobile': 'mobile', 'desktop-app': 'desktop', diff --git a/packages/ui-client/src/components/Header/HeaderIcon.tsx b/packages/ui-client/src/components/Header/HeaderIcon.tsx index 0cf8edf9358c..055e5d74eff5 100644 --- a/packages/ui-client/src/components/Header/HeaderIcon.tsx +++ b/packages/ui-client/src/components/Header/HeaderIcon.tsx @@ -1,8 +1,9 @@ import { Box, Icon } from '@rocket.chat/fuselage'; -import type { ComponentProps, FC, ReactElement } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import type { FC, ReactElement } from 'react'; import { isValidElement } from 'react'; -type HeaderIconProps = { icon: ReactElement | { name: ComponentProps<typeof Icon>['name']; color?: string } | null }; +type HeaderIconProps = { icon: ReactElement | { name: IconName; color?: string } | null }; const HeaderIcon: FC<HeaderIconProps> = ({ icon }) => icon && ( diff --git a/packages/ui-video-conf/src/VideoConfButton.tsx b/packages/ui-video-conf/src/VideoConfButton.tsx index 8ca73c154146..9273b4b1c559 100644 --- a/packages/ui-video-conf/src/VideoConfButton.tsx +++ b/packages/ui-video-conf/src/VideoConfButton.tsx @@ -1,8 +1,9 @@ -import { Button, Icon, IconProps } from '@rocket.chat/fuselage'; +import { Button, Icon } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactNode, ReactElement, ButtonHTMLAttributes } from 'react'; type VideoConfButtonProps = { - icon?: IconProps['name']; + icon?: IconName; primary?: boolean; secondary?: boolean; danger?: boolean; diff --git a/packages/ui-video-conf/src/VideoConfController.tsx b/packages/ui-video-conf/src/VideoConfController.tsx index 3dcb991818eb..b13ce13df55f 100644 --- a/packages/ui-video-conf/src/VideoConfController.tsx +++ b/packages/ui-video-conf/src/VideoConfController.tsx @@ -1,10 +1,10 @@ import { IconButton } from '@rocket.chat/fuselage'; -import type { IconProps } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactElement, ButtonHTMLAttributes } from 'react'; type VideoConfControllerProps = { - icon: IconProps['name']; + icon: IconName; active?: boolean; secondary?: boolean; disabled?: boolean; diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index 2390f6b25a68..8f8a8d3ecd56 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -1,8 +1,9 @@ import { Button, Icon } from '@rocket.chat/fuselage'; import type { LoginService } from '@rocket.chat/ui-contexts'; import { useLoginWithService, useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement, ComponentProps, SetStateAction, Dispatch } from 'react'; +import type { ReactElement, SetStateAction, Dispatch } from 'react'; import { useCallback } from 'react'; +import type { Keys as IconName } from '@rocket.chat/icons'; import type { LoginErrors } from './LoginForm'; @@ -43,7 +44,7 @@ const LoginServicesButton = <T extends LoginService>({ display='flex' justifyContent='center' > - {icon && <Icon size='x20' mie='x4' name={icon as ComponentProps<typeof Icon>['name']} />} + {icon && <Icon size='x20' mie='x4' name={icon as IconName} />} {buttonLabelText || t('Sign_in_with__provider__', { provider: title })} </Button> ); From ad08c26b465ec3bac3c73377c0d10c160a0aeacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Guimar=C3=A3es=20Ribeiro?= <henrique.jobs1@gmail.com> Date: Thu, 20 Jul 2023 23:23:21 -0300 Subject: [PATCH 138/149] feat: Create upsell options in the admin sidebar and reorganize it (#29731) --- .changeset/tidy-bears-camp.md | 5 + .../server/constant/permissions.ts | 2 + .../client/components/GenericUpsellModal.tsx | 105 +++++++++++++++ .../client/components/UpsellModal.stories.tsx | 12 +- apps/meteor/client/components/UpsellModal.tsx | 75 ----------- .../views/admin/AdministrationRouter.tsx | 2 +- .../admin/customEmoji/CustomEmojiRoute.tsx | 2 +- .../admin/customSounds/CustomSoundsPage.tsx | 2 +- .../FederationDashboardPage.tsx | 2 +- .../views/admin/info/InformationPage.tsx | 2 +- .../views/admin/info/InformationRoute.tsx | 2 +- .../moderation/ModerationConsolePage.tsx | 2 +- .../views/admin/oauthApps/OAuthAppsPage.tsx | 2 +- apps/meteor/client/views/admin/routes.tsx | 66 ++++++---- .../meteor/client/views/admin/sidebarItems.ts | 122 ++++++++++-------- .../views/admin/viewLogs/ViewLogsPage.tsx | 2 +- .../marketplace/UnlimitedAppsUpsellModal.tsx | 31 +---- .../components/MarketplaceHeader.tsx | 3 +- .../ee/client/startup/deviceManagement.ts | 21 --- .../ee/client/startup/engagementDashboard.ts | 39 ------ apps/meteor/ee/client/startup/index.ts | 1 - .../DeviceManagementAdminRoute.tsx | 47 ++++++- .../EngagementDashboardPage.tsx | 2 +- .../EngagementDashboardRoute.tsx | 83 ++++++++---- .../ee/server/lib/deviceManagement/startup.ts | 1 - .../server/lib/engagementDashboard/startup.ts | 6 - .../ee/server/startup/engagementDashboard.ts | 3 +- .../rocketchat-i18n/i18n/en.i18n.json | 20 +-- .../public/images/device-management.png | Bin 0 -> 40365 bytes apps/meteor/public/images/engagement.png | Bin 0 -> 42933 bytes apps/meteor/server/startup/migrations/v302.ts | 9 ++ .../tests/e2e/administration-menu.spec.ts | 2 +- apps/meteor/tests/e2e/administration.spec.ts | 4 +- 33 files changed, 367 insertions(+), 310 deletions(-) create mode 100644 .changeset/tidy-bears-camp.md create mode 100644 apps/meteor/client/components/GenericUpsellModal.tsx delete mode 100644 apps/meteor/client/components/UpsellModal.tsx delete mode 100644 apps/meteor/ee/client/startup/engagementDashboard.ts create mode 100644 apps/meteor/public/images/device-management.png create mode 100644 apps/meteor/public/images/engagement.png create mode 100644 apps/meteor/server/startup/migrations/v302.ts diff --git a/.changeset/tidy-bears-camp.md b/.changeset/tidy-bears-camp.md new file mode 100644 index 000000000000..3c2013f79023 --- /dev/null +++ b/.changeset/tidy-bears-camp.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +Introduced upsells for the engagement dashboard and device management admin sidebar items in CE workspaces. Additionally, restructured the admin sidebar items to enhance organization. diff --git a/apps/meteor/app/authorization/server/constant/permissions.ts b/apps/meteor/app/authorization/server/constant/permissions.ts index 0090d10fd880..fc917028c33f 100644 --- a/apps/meteor/app/authorization/server/constant/permissions.ts +++ b/apps/meteor/app/authorization/server/constant/permissions.ts @@ -69,6 +69,8 @@ export const permissions = [ { _id: 'view-c-room', roles: ['admin', 'user', 'bot', 'app', 'anonymous'] }, { _id: 'user-generate-access-token', roles: ['admin'] }, { _id: 'view-d-room', roles: ['admin', 'user', 'bot', 'app', 'guest'] }, + { _id: 'view-device-management', roles: ['admin'] }, + { _id: 'view-engagement-dashboard', roles: ['admin'] }, { _id: 'view-full-other-user-info', roles: ['admin'] }, { _id: 'view-history', roles: ['admin', 'user', 'anonymous'] }, { _id: 'view-joined-room', roles: ['guest', 'bot', 'app', 'anonymous'] }, diff --git a/apps/meteor/client/components/GenericUpsellModal.tsx b/apps/meteor/client/components/GenericUpsellModal.tsx new file mode 100644 index 000000000000..7e24219dafb3 --- /dev/null +++ b/apps/meteor/client/components/GenericUpsellModal.tsx @@ -0,0 +1,105 @@ +import { Box, Button, Modal } from '@rocket.chat/fuselage'; +import type { Keys as IconName } from '@rocket.chat/icons'; +import { useRouter, useSetModal, useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import type { ReactNode, ReactElement, ComponentProps } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; + +type UpsellModalProps = { + children?: ReactNode; + tagline?: ReactNode; + cancelText?: ReactNode; + confirmText?: ReactNode; + title: string | ReactElement; + subtitle?: string | ReactElement; + description?: string | ReactElement; + icon?: IconName; + img: ComponentProps<typeof Modal.HeroImage>['src']; + onCancel?: () => void; + onClose?: () => void; + onConfirm?: () => void; + onCloseEffect?: () => void; +}; + +const GenericUpsellModal = ({ + tagline, + title, + subtitle, + img, + cancelText, + confirmText, + icon, + description, + onCancel, + onConfirm, + onClose = onCancel, + onCloseEffect, +}: UpsellModalProps) => { + const t = useTranslation(); + const cloudWorkspaceHadTrial = Boolean(useSetting('Cloud_Workspace_Had_Trial')); + + const router = useRouter(); + const setModal = useSetModal(); + + const handleModalClose = useCallback(() => { + setModal(null); + }, [setModal]); + + const handleConfirmModal = useCallback(() => { + handleModalClose(); + router.navigate({ + pathname: '/admin/upgrade/go-fully-featured-registered', + }); + }, [handleModalClose, router]); + + const talkToSales = 'https://go.rocket.chat/i/contact-sales'; + const handleCancelModal = useCallback(() => { + handleModalClose(); + window.open(talkToSales, '_blank'); + }, [handleModalClose]); + + const onCloseRef = useRef(onCloseEffect ?? handleModalClose); + onCloseRef.current = onCloseEffect ?? handleModalClose; + + useEffect(() => { + return () => { + const onClose = onCloseRef.current; + onClose?.(); + }; + }, []); + + return ( + <Modal> + <Modal.Header> + {icon && <Modal.Icon name={icon} />} + <Modal.HeaderText> + <Modal.Tagline color='font-annotation'>{tagline ?? t('Enterprise_capability')}</Modal.Tagline> + <Modal.Title>{title}</Modal.Title> + </Modal.HeaderText> + <Modal.Close title={t('Close')} onClick={onClose ?? handleModalClose} /> + </Modal.Header> + <Modal.Content> + <Modal.HeroImage src={img} /> + {subtitle && ( + <Box is='h3' fontScale='h3' mbe='x16'> + {subtitle} + </Box> + )} + + {description && <Box fontScale='p2'>{description}</Box>} + </Modal.Content> + <Modal.Footer> + <Modal.FooterControllers> + <Button secondary onClick={onCancel ?? handleCancelModal}> + {cancelText ?? t('Talk_to_an_expert')} + </Button> + + <Button primary onClick={onConfirm ?? handleConfirmModal}> + {confirmText ?? cloudWorkspaceHadTrial ? t('Learn_more') : t('Start_a_free_trial')} + </Button> + </Modal.FooterControllers> + </Modal.Footer> + </Modal> + ); +}; + +export default GenericUpsellModal; diff --git a/apps/meteor/client/components/UpsellModal.stories.tsx b/apps/meteor/client/components/UpsellModal.stories.tsx index 177efdab2876..0e72c7c89647 100644 --- a/apps/meteor/client/components/UpsellModal.stories.tsx +++ b/apps/meteor/client/components/UpsellModal.stories.tsx @@ -2,15 +2,15 @@ import { action } from '@storybook/addon-actions'; import type { ComponentMeta, ComponentStory } from '@storybook/react'; import React from 'react'; -import UpsellModal from './UpsellModal'; +import GenericUpsellModal from './GenericUpsellModal'; export default { - title: 'Components/UpsellModal', - component: UpsellModal, -} as ComponentMeta<typeof UpsellModal>; + title: 'Components/GenericUpsellModal', + component: GenericUpsellModal, +} as ComponentMeta<typeof GenericUpsellModal>; -export const Example: ComponentStory<typeof UpsellModal> = () => ( - <UpsellModal +export const Example: ComponentStory<typeof GenericUpsellModal> = () => ( + <GenericUpsellModal title='Title' subtitle='Subtitle' description='The quick brown fox jumps over the lazy dog.' diff --git a/apps/meteor/client/components/UpsellModal.tsx b/apps/meteor/client/components/UpsellModal.tsx deleted file mode 100644 index ad34db316784..000000000000 --- a/apps/meteor/client/components/UpsellModal.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { Box, Button, Modal } from '@rocket.chat/fuselage'; -import type { Keys as IconName } from '@rocket.chat/icons'; -import { useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactNode, ReactElement, ComponentProps } from 'react'; -import React from 'react'; - -type UpsellModalProps = { - children?: ReactNode; - tagline?: ReactNode; - cancelText?: ReactNode; - confirmText?: ReactNode; - title: string | ReactElement; - subtitle?: string | ReactElement; - description?: string | ReactElement; - icon?: IconName; - img: ComponentProps<typeof Modal.HeroImage>['src']; - onCancel?: () => void; - onClose?: () => void; - onConfirm: () => void; -}; - -const UpsellModal = ({ - tagline, - title, - subtitle, - img, - cancelText, - confirmText, - icon, - description, - onCancel, - onConfirm, - onClose = onCancel, -}: UpsellModalProps) => { - const t = useTranslation(); - - return ( - <Modal> - <Modal.Header> - {icon && <Modal.Icon name={icon} />} - <Modal.HeaderText> - <Modal.Tagline color='font-annotation'>{tagline ?? t('Enterprise_capability')}</Modal.Tagline> - <Modal.Title>{title}</Modal.Title> - </Modal.HeaderText> - <Modal.Close title={t('Close')} onClick={onClose} /> - </Modal.Header> - <Modal.Content> - <Modal.HeroImage src={img} /> - {subtitle && ( - <Box is='h3' fontScale='h3'> - {subtitle} - </Box> - )} - <br /> - {description && <Box fontScale='p2'>{description}</Box>} - </Modal.Content> - <Modal.Footer> - <Modal.FooterControllers> - {onCancel && ( - <Button secondary onClick={onCancel}> - {cancelText ?? t('Close')} - </Button> - )} - {onConfirm && ( - <Button primary onClick={onConfirm}> - {confirmText ?? t('Talk_to_sales')} - </Button> - )} - </Modal.FooterControllers> - </Modal.Footer> - </Modal> - ); -}; - -export default UpsellModal; diff --git a/apps/meteor/client/views/admin/AdministrationRouter.tsx b/apps/meteor/client/views/admin/AdministrationRouter.tsx index 93ea650d0d33..9a86f6ea61f7 100644 --- a/apps/meteor/client/views/admin/AdministrationRouter.tsx +++ b/apps/meteor/client/views/admin/AdministrationRouter.tsx @@ -49,7 +49,7 @@ const AdministrationRouter = ({ children }: AdministrationRouterProps): ReactEle return; } - const defaultRoutePath = getAdminSidebarItems().find(firstSidebarPage)?.href ?? '/admin/info'; + const defaultRoutePath = getAdminSidebarItems().find(firstSidebarPage)?.href ?? '/admin/workspace'; if (isGoRocketChatLink(defaultRoutePath)) { window.open(defaultRoutePath, '_blank'); diff --git a/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx b/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx index 848e0421a014..26b74063e025 100644 --- a/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx +++ b/apps/meteor/client/views/admin/customEmoji/CustomEmojiRoute.tsx @@ -45,7 +45,7 @@ const CustomEmojiRoute = (): ReactElement => { return ( <Page flexDirection='row'> <Page name='admin-emoji-custom'> - <Page.Header title={t('Custom_Emoji')}> + <Page.Header title={t('Emoji')}> <Button primary onClick={handleAddEmoji} aria-label={t('New')}> {t('New')} </Button> diff --git a/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx b/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx index 1225d6789592..82191963cbd2 100644 --- a/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx +++ b/apps/meteor/client/views/admin/customSounds/CustomSoundsPage.tsx @@ -41,7 +41,7 @@ const CustomSoundsPage = () => { return ( <Page flexDirection='row'> <Page name='admin-custom-sounds'> - <Page.Header title={t('Custom_Sounds')}> + <Page.Header title={t('Sounds')}> <Button primary onClick={handleNewButtonClick} aria-label={t('New')}> {t('New')} </Button> diff --git a/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx b/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx index 43d517fee432..8b33a2734a6e 100644 --- a/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx +++ b/apps/meteor/client/views/admin/federationDashboard/FederationDashboardPage.tsx @@ -12,7 +12,7 @@ function FederationDashboardPage(): ReactElement { return ( <Page> - <Page.Header title={t('Federation_Dashboard')} /> + <Page.Header title={t('Federation')} /> <Page.ScrollableContentWithShadow> <Box margin='x24'> <OverviewSection /> diff --git a/apps/meteor/client/views/admin/info/InformationPage.tsx b/apps/meteor/client/views/admin/info/InformationPage.tsx index 2bf9d5504f37..d801e2379622 100644 --- a/apps/meteor/client/views/admin/info/InformationPage.tsx +++ b/apps/meteor/client/views/admin/info/InformationPage.tsx @@ -42,7 +42,7 @@ const InformationPage = memo(function InformationPage({ return ( <Page data-qa='admin-info' bg='tint'> - <Page.Header title={t('Info')}> + <Page.Header title={t('Workspace')}> {canViewStatistics && ( <ButtonGroup> <Button type='button' onClick={onClickDownloadInfo}> diff --git a/apps/meteor/client/views/admin/info/InformationRoute.tsx b/apps/meteor/client/views/admin/info/InformationRoute.tsx index 029c013284fc..97b7faa79796 100644 --- a/apps/meteor/client/views/admin/info/InformationRoute.tsx +++ b/apps/meteor/client/views/admin/info/InformationRoute.tsx @@ -83,7 +83,7 @@ const InformationRoute = (): ReactElement => { if (error || !statistics) { return ( <Page> - <Page.Header title={t('Info')}> + <Page.Header title={t('Workspace')}> <ButtonGroup> <Button primary type='button' onClick={handleClickRefreshButton}> <Icon name='reload' /> {t('Refresh')} diff --git a/apps/meteor/client/views/admin/moderation/ModerationConsolePage.tsx b/apps/meteor/client/views/admin/moderation/ModerationConsolePage.tsx index 34002c31b36f..1d6433d96d96 100644 --- a/apps/meteor/client/views/admin/moderation/ModerationConsolePage.tsx +++ b/apps/meteor/client/views/admin/moderation/ModerationConsolePage.tsx @@ -27,7 +27,7 @@ const ModerationConsolePage = () => { return ( <Page flexDirection='row'> <Page> - <Page.Header title={t('Moderation_Console')} /> + <Page.Header title={t('Moderation')} /> <Page.Content> <ModerationConsoleTable /> </Page.Content> diff --git a/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx b/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx index 0c4e91466e18..36648f168614 100644 --- a/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx +++ b/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx @@ -19,7 +19,7 @@ const OAuthAppsPage = (): ReactElement => { return ( <Page flexDirection='row'> <Page> - <Page.Header title={t('OAuth_Applications')}> + <Page.Header title={t('Third_party_login')}> {context && ( <Button alignSelf='flex-end' onClick={(): void => router.push({})}> <Icon name='back' /> diff --git a/apps/meteor/client/views/admin/routes.tsx b/apps/meteor/client/views/admin/routes.tsx index 354604c3bb11..7b85e34a8289 100644 --- a/apps/meteor/client/views/admin/routes.tsx +++ b/apps/meteor/client/views/admin/routes.tsx @@ -10,12 +10,12 @@ declare module '@rocket.chat/ui-contexts' { pattern: '/admin'; }; 'custom-sounds': { - pathname: `/admin/custom-sounds${`/${string}` | ''}${`/${string}` | ''}`; - pattern: '/admin/custom-sounds/:context?/:id?'; + pathname: `/admin/sounds${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/sounds/:context?/:id?'; }; 'admin-info': { - pathname: '/admin/info'; - pattern: '/admin/info'; + pathname: '/admin/workspace'; + pattern: '/admin/workspace'; }; 'admin-import': { pathname: '/admin/import'; @@ -38,8 +38,8 @@ declare module '@rocket.chat/ui-contexts' { pattern: '/admin/mailer'; }; 'admin-oauth-apps': { - pathname: `/admin/oauth-apps${`/${'new' | 'edit'}` | ''}${`/${string}` | ''}`; - pattern: '/admin/oauth-apps/:context?/:id?'; + pathname: `/admin/third-party-login${`/${'new' | 'edit'}` | ''}${`/${string}` | ''}`; + pattern: '/admin/third-party-login/:context?/:id?'; }; 'admin-integrations': { pathname: `/admin/integrations${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}`; @@ -50,8 +50,8 @@ declare module '@rocket.chat/ui-contexts' { pattern: '/admin/user-status/:context?/:id?'; }; 'emoji-custom': { - pathname: `/admin/emoji-custom${`/${string}` | ''}${`/${string}` | ''}`; - pattern: '/admin/emoji-custom/:context?/:id?'; + pathname: `/admin/emoji${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/emoji/:context?/:id?'; }; 'admin-users': { pathname: `/admin/users${`/${string}` | ''}${`/${string}` | ''}`; @@ -66,16 +66,16 @@ declare module '@rocket.chat/ui-contexts' { pattern: '/admin/invites'; }; 'cloud': { - pathname: `/admin/cloud${`/${string}` | ''}`; - pattern: '/admin/cloud/:page?'; + pathname: `/admin/registration${`/${string}` | ''}`; + pattern: '/admin/registration/:page?'; }; 'admin-view-logs': { - pathname: '/admin/view-logs'; - pattern: '/admin/view-logs'; + pathname: '/admin/logs'; + pattern: '/admin/logs'; }; 'federation-dashboard': { - pathname: '/admin/federation-dashboard'; - pattern: '/admin/federation-dashboard'; + pathname: '/admin/federation'; + pattern: '/admin/federation'; }; 'admin-permissions': { pathname: `/admin/permissions${`/${string}` | ''}${`/${string}` | ''}`; @@ -89,13 +89,21 @@ declare module '@rocket.chat/ui-contexts' { pathname: `/admin/settings${`/${string}` | ''}`; pattern: '/admin/settings/:group?'; }; + 'device-management': { + pathname: `/admin/device-management${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/device-management/:context?/:id?'; + }; + 'engagement-dashboard': { + pathname: `/admin/engagement${`/${string}` | ''}`; + pattern: '/admin/engagement/:tab?'; + }; 'upgrade': { pathname: `/admin/upgrade${`/${UpgradeTabVariant}` | ''}`; pattern: '/admin/upgrade/:type?'; }; 'moderation-console': { - pathname: `/admin/moderation-console${`/${string}` | ''}${`/${string}` | ''}`; - pattern: '/admin/moderation-console/:context?/:id?'; + pathname: `/admin/moderation${`/${string}` | ''}${`/${string}` | ''}`; + pattern: '/admin/moderation/:context?/:id?'; }; } } @@ -106,12 +114,12 @@ export const registerAdminRoute = createRouteGroup( lazy(() => import('./AdministrationRouter')), ); -registerAdminRoute('/custom-sounds/:context?/:id?', { +registerAdminRoute('/sounds/:context?/:id?', { name: 'custom-sounds', component: lazy(() => import('./customSounds/CustomSoundsRoute')), }); -registerAdminRoute('/info', { +registerAdminRoute('/workspace', { name: 'admin-info', component: lazy(() => import('./info/InformationRoute')), }); @@ -145,7 +153,7 @@ registerAdminRoute('/mailer', { component: lazy(() => import('./mailer/MailerRoute')), }); -registerAdminRoute('/oauth-apps/:context?/:id?', { +registerAdminRoute('/third-party-login/:context?/:id?', { name: 'admin-oauth-apps', component: lazy(() => import('./oauthApps/OAuthAppsRoute')), }); @@ -160,7 +168,7 @@ registerAdminRoute('/user-status/:context?/:id?', { component: lazy(() => import('./customUserStatus/CustomUserStatusRoute')), }); -registerAdminRoute('/emoji-custom/:context?/:id?', { +registerAdminRoute('/emoji/:context?/:id?', { name: 'emoji-custom', component: lazy(() => import('./customEmoji/CustomEmojiRoute')), }); @@ -180,17 +188,17 @@ registerAdminRoute('/invites', { component: lazy(() => import('./invites/InvitesRoute')), }); -registerAdminRoute('/cloud/:page?', { +registerAdminRoute('/registration/:page?', { name: 'cloud', component: lazy(() => import('./cloud/CloudRoute')), }); -registerAdminRoute('/view-logs', { +registerAdminRoute('/logs', { name: 'admin-view-logs', component: lazy(() => import('./viewLogs/ViewLogsRoute')), }); -registerAdminRoute('/federation-dashboard', { +registerAdminRoute('/federation', { name: 'federation-dashboard', component: lazy(() => import('./federationDashboard/FederationDashboardRoute')), }); @@ -215,7 +223,17 @@ registerAdminRoute('/upgrade/:type?', { component: lazy(() => import('./upgrade/UpgradePage')), }); -registerAdminRoute('/moderation-console/:context?/:id?', { +registerAdminRoute('/moderation/:context?/:id?', { name: 'moderation-console', component: lazy(() => import('./moderation/ModerationConsoleRoute')), }); + +registerAdminRoute('/engagement/:tab?', { + name: 'engagement-dashboard', + component: lazy(() => import('../../../ee/client/views/admin/engagementDashboard/EngagementDashboardRoute')), +}); + +registerAdminRoute('/device-management/:context?/:id?', { + name: 'device-management', + component: lazy(() => import('../../../ee/client/views/admin/deviceManagement/DeviceManagementAdminRoute')), +}); diff --git a/apps/meteor/client/views/admin/sidebarItems.ts b/apps/meteor/client/views/admin/sidebarItems.ts index 5cb699934d13..c397e28e6db1 100644 --- a/apps/meteor/client/views/admin/sidebarItems.ts +++ b/apps/meteor/client/views/admin/sidebarItems.ts @@ -8,29 +8,35 @@ export const { subscribeToSidebarItems: subscribeToAdminSidebarItems, } = createSidebarItems([ { - href: '/admin/info', - i18nLabel: 'Info', + href: '/admin/workspace', + i18nLabel: 'Workspace', icon: 'info-circled', permissionGranted: (): boolean => hasPermission('view-statistics'), }, { + href: '/admin/registration', + i18nLabel: 'Registration', + icon: 'cloud-plus', + permissionGranted: (): boolean => hasPermission('manage-cloud'), + }, + { + href: '/admin/engagement/users', + i18nLabel: 'Engagement', + icon: 'dashboard', + permissionGranted: (): boolean => hasPermission('view-engagement-dashboard'), + }, + { + href: '/admin/moderation', + i18nLabel: 'Moderation', icon: 'shield-alt', - href: '/admin/moderation-console', - i18nLabel: 'Moderation console', tag: 'Beta', permissionGranted: (): boolean => hasPermission('view-moderation-console'), }, { - href: '/admin/import', - i18nLabel: 'Import', - icon: 'import', - permissionGranted: (): boolean => hasPermission('run-import'), - }, - { - href: '/admin/users', - i18nLabel: 'Users', - icon: 'team', - permissionGranted: (): boolean => hasPermission('view-user-administration'), + href: '/admin/federation', + i18nLabel: 'Federation', + icon: 'discover', + permissionGranted: (): boolean => hasPermission('view-federation-data'), }, { href: '/admin/rooms', @@ -38,6 +44,12 @@ export const { icon: 'hashtag', permissionGranted: (): boolean => hasPermission('view-room-administration'), }, + { + href: '/admin/users', + i18nLabel: 'Users', + icon: 'team', + permissionGranted: (): boolean => hasPermission('view-user-administration'), + }, { href: '/admin/invites', i18nLabel: 'Invites', @@ -45,46 +57,46 @@ export const { permissionGranted: (): boolean => hasPermission('create-invite-links'), }, { - href: '/admin/cloud', - icon: 'cloud-plus', - i18nLabel: 'Registration', - permissionGranted: (): boolean => hasPermission('manage-cloud'), + href: '/admin/user-status', + i18nLabel: 'User_Status', + icon: 'user', + permissionGranted: (): boolean => hasAtLeastOnePermission(['manage-user-status']), }, { - href: '/admin/view-logs', - i18nLabel: 'View_Logs', - icon: 'post', - permissionGranted: (): boolean => hasPermission('view-logs'), + href: '/admin/permissions', + i18nLabel: 'Permissions', + icon: 'user-lock', + permissionGranted: (): boolean => hasAtLeastOnePermission(['access-permissions', 'access-setting-permissions']), }, { - href: '/admin/custom-sounds', - i18nLabel: 'Custom_Sounds', - icon: 'volume', - permissionGranted: (): boolean => hasPermission('manage-sounds'), - }, - { - href: '/admin/federation-dashboard', - icon: 'discover', - i18nLabel: 'Federation Dashboard', - permissionGranted: (): boolean => hasPermission('view-federation-data'), + href: '/admin/device-management', + i18nLabel: 'Device_Management', + icon: 'mobile', + permissionGranted: (): boolean => hasPermission('view-device-management'), }, { href: '/admin/email-inboxes', - icon: 'mail', i18nLabel: 'Email_Inboxes', + icon: 'mail', tag: 'Alpha', permissionGranted: (): boolean => hasPermission('manage-email-inbox'), }, { - href: '/admin/emoji-custom', - icon: 'emoji', - i18nLabel: 'Custom_Emoji', - permissionGranted: (): boolean => hasPermission('manage-emoji'), + href: '/admin/mailer', + icon: 'mail', + i18nLabel: 'Mailer', + permissionGranted: (): boolean => hasAllPermission('access-mailer'), + }, + { + href: '/admin/third-party-login', + i18nLabel: 'Third_party_login', + icon: 'login', + permissionGranted: (): boolean => hasAllPermission('manage-oauth-apps'), }, { href: '/admin/integrations', - icon: 'code', i18nLabel: 'Integrations', + icon: 'code', permissionGranted: (): boolean => hasAtLeastOnePermission([ 'manage-outgoing-integrations', @@ -94,33 +106,33 @@ export const { ]), }, { - href: '/admin/oauth-apps', - icon: 'discover', - i18nLabel: 'OAuth Apps', - permissionGranted: (): boolean => hasAllPermission('manage-oauth-apps'), + href: '/admin/import', + i18nLabel: 'Import', + icon: 'import', + permissionGranted: (): boolean => hasPermission('run-import'), }, { - href: '/admin/mailer', - icon: 'mail', - i18nLabel: 'Mailer', - permissionGranted: (): boolean => hasAllPermission('access-mailer'), + href: '/admin/logs', + i18nLabel: 'Logs', + icon: 'post', + permissionGranted: (): boolean => hasPermission('view-logs'), }, { - href: '/admin/user-status', - icon: 'user', - i18nLabel: 'User_Status', - permissionGranted: (): boolean => hasAtLeastOnePermission(['manage-user-status']), + href: '/admin/sounds', + i18nLabel: 'Sounds', + icon: 'volume', + permissionGranted: (): boolean => hasPermission('manage-sounds'), }, { - href: '/admin/permissions', - icon: 'lock', - i18nLabel: 'Permissions', - permissionGranted: (): boolean => hasAtLeastOnePermission(['access-permissions', 'access-setting-permissions']), + href: '/admin/emoji', + i18nLabel: 'Emoji', + icon: 'emoji', + permissionGranted: (): boolean => hasPermission('manage-emoji'), }, { href: '/admin/settings', - icon: 'customize', i18nLabel: 'Settings', + icon: 'customize', permissionGranted: (): boolean => hasAtLeastOnePermission(['view-privileged-setting', 'edit-privileged-setting', 'manage-selected-settings']), }, diff --git a/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx b/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx index 5d86658f2975..a75c22da19b0 100644 --- a/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx +++ b/apps/meteor/client/views/admin/viewLogs/ViewLogsPage.tsx @@ -10,7 +10,7 @@ const ViewLogsPage = (): ReactElement => { return ( <Page> - <Page.Header title={t('View_Logs')} /> + <Page.Header title={t('Logs')} /> <Page.Content> <ServerLogs /> </Page.Content> diff --git a/apps/meteor/client/views/marketplace/UnlimitedAppsUpsellModal.tsx b/apps/meteor/client/views/marketplace/UnlimitedAppsUpsellModal.tsx index e5dc523cabc4..ec3037fa37b9 100644 --- a/apps/meteor/client/views/marketplace/UnlimitedAppsUpsellModal.tsx +++ b/apps/meteor/client/views/marketplace/UnlimitedAppsUpsellModal.tsx @@ -1,40 +1,19 @@ -import { useSetting, useTranslation, useRoute } from '@rocket.chat/ui-contexts'; -import React, { useCallback } from 'react'; +import { useSetting, useTranslation } from '@rocket.chat/ui-contexts'; +import React from 'react'; -import UpsellModal from '../../components/UpsellModal'; +import GenericUpsellModal from '../../components/GenericUpsellModal'; -type UnlimitedAppsUpsellModalProps = { - onClose: () => void; -}; - -const UnlimitedAppsUpsellModal = ({ onClose }: UnlimitedAppsUpsellModalProps) => { +const UnlimitedAppsUpsellModal = () => { const t = useTranslation(); const cloudWorkspaceHadTrial = useSetting('Cloud_Workspace_Had_Trial') as boolean; - const talkToSales = 'https://go.rocket.chat/i/contact-sales'; - - const upgradeRoute = useRoute('upgrade'); - - const goFullyFeaturedRegistered = useCallback(() => { - upgradeRoute.push({ type: 'go-fully-featured-registered' }); - onClose(); - }, [upgradeRoute, onClose]); - - const goToTalkSales = useCallback(() => { - window.open(talkToSales, '_blank'); - onClose(); - }, [onClose, talkToSales]); return ( - <UpsellModal + <GenericUpsellModal title={t('Enable_unlimited_apps')} img='images/unlimited-apps-modal.svg' subtitle={t('Get_all_apps')} description={!cloudWorkspaceHadTrial ? t('Workspaces_on_community_edition_trial_on') : t('Workspaces_on_community_edition_trial_off')} - confirmText={!cloudWorkspaceHadTrial ? t('Start_free_trial') : t('Learn_more')} cancelText={t('Talk_to_sales')} - onConfirm={goFullyFeaturedRegistered} - onCancel={goToTalkSales} - onClose={onClose} /> ); }; diff --git a/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx b/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx index ec06f1afff13..e0c12b73cf9b 100644 --- a/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx +++ b/apps/meteor/client/views/marketplace/components/MarketplaceHeader.tsx @@ -16,7 +16,6 @@ const MarketplaceHeader = ({ title }: { title: string }): ReactElement | null => const route = useRoute('marketplace'); const setModal = useSetModal(); const result = useAppsCountQuery(context); - const handleModalClose = useCallback(() => setModal(null), [setModal]); const handleUploadButtonClick = useCallback((): void => { route.push({ context, page: 'install' }); @@ -34,7 +33,7 @@ const MarketplaceHeader = ({ title }: { title: string }): ReactElement | null => {isAdmin && result.isSuccess && !result.data.hasUnlimitedApps && ( <Button onClick={() => { - setModal(<UnlimitedAppsUpsellModal onClose={handleModalClose} />); + setModal(<UnlimitedAppsUpsellModal />); }} > {t('Enable_unlimited_apps')} diff --git a/apps/meteor/ee/client/startup/deviceManagement.ts b/apps/meteor/ee/client/startup/deviceManagement.ts index 33f1a99dd767..3c22aa14384b 100644 --- a/apps/meteor/ee/client/startup/deviceManagement.ts +++ b/apps/meteor/ee/client/startup/deviceManagement.ts @@ -1,16 +1,10 @@ import { lazy } from 'react'; -import { hasAllPermission } from '../../../app/authorization/client'; import { registerAccountRoute, registerAccountSidebarItem, unregisterSidebarItem } from '../../../client/views/account'; -import { registerAdminRoute, registerAdminSidebarItem, unregisterAdminSidebarItem } from '../../../client/views/admin'; import { onToggledFeature } from '../lib/onToggledFeature'; declare module '@rocket.chat/ui-contexts' { interface IRouterPaths { - 'device-management': { - pathname: `/admin/device-management${`/${string}` | ''}${`/${string}` | ''}`; - pattern: '/admin/device-management/:context?/:id?'; - }; 'manage-devices': { pathname: '/account/manage-devices'; pattern: '/account/manage-devices'; @@ -18,12 +12,6 @@ declare module '@rocket.chat/ui-contexts' { } } -const [registerAdminRouter, unregisterAdminRouter] = registerAdminRoute('/device-management/:context?/:id?', { - name: 'device-management', - component: lazy(() => import('../views/admin/deviceManagement/DeviceManagementAdminRoute')), - ready: false, -}); - const [registerAccountRouter, unregisterAccountRouter] = registerAccountRoute('/manage-devices', { name: 'manage-devices', component: lazy(() => import('../views/account/deviceManagement/DeviceManagementAccountPage')), @@ -31,24 +19,15 @@ const [registerAccountRouter, unregisterAccountRouter] = registerAccountRoute('/ onToggledFeature('device-management', { up: () => { - registerAdminSidebarItem({ - href: '/admin/device-management', - i18nLabel: 'Device_Management', - icon: 'mobile', - permissionGranted: () => hasAllPermission('view-device-management'), - }); registerAccountSidebarItem({ href: '/account/manage-devices', i18nLabel: 'Manage_Devices', icon: 'mobile', }); - registerAdminRouter(); registerAccountRouter(); }, down: () => { - unregisterAdminSidebarItem('Device_Management'); unregisterSidebarItem('Manage_Devices'); - unregisterAdminRouter(); unregisterAccountRouter(); }, }); diff --git a/apps/meteor/ee/client/startup/engagementDashboard.ts b/apps/meteor/ee/client/startup/engagementDashboard.ts deleted file mode 100644 index 0bd1d4995169..000000000000 --- a/apps/meteor/ee/client/startup/engagementDashboard.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { lazy } from 'react'; - -import { hasAllPermission } from '../../../app/authorization/client'; -import { registerAdminRoute, registerAdminSidebarItem, unregisterAdminSidebarItem } from '../../../client/views/admin'; -import { onToggledFeature } from '../lib/onToggledFeature'; - -declare module '@rocket.chat/ui-contexts' { - interface IRouterPaths { - 'engagement-dashboard': { - pattern: '/admin/engagement-dashboard/:tab?'; - pathname: `/admin/engagement-dashboard${`/${string}` | ''}`; - }; - } -} - -const [registerRoute, unregisterRoute] = registerAdminRoute('/engagement-dashboard/:tab?', { - name: 'engagement-dashboard', - component: lazy(() => import('../views/admin/engagementDashboard/EngagementDashboardRoute')), - ready: false, -}); - -onToggledFeature('engagement-dashboard', { - up: () => - Meteor.startup(() => { - registerAdminSidebarItem({ - href: '/admin/engagement-dashboard', - i18nLabel: 'Engagement Dashboard', - icon: 'file-keynote', - permissionGranted: () => hasAllPermission('view-engagement-dashboard'), - }); - registerRoute(); - }), - down: () => - Meteor.startup(() => { - unregisterAdminSidebarItem('Engagement Dashboard'); - unregisterRoute(); - }), -}); diff --git a/apps/meteor/ee/client/startup/index.ts b/apps/meteor/ee/client/startup/index.ts index c77a0f6ea160..a24d22cb9615 100644 --- a/apps/meteor/ee/client/startup/index.ts +++ b/apps/meteor/ee/client/startup/index.ts @@ -1,6 +1,5 @@ import '../apps'; import './audit'; import './deviceManagement'; -import './engagementDashboard'; import './slashCommands'; import './readReceipt'; diff --git a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminRoute.tsx b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminRoute.tsx index eb743fcad703..7f55acacebfd 100644 --- a/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminRoute.tsx +++ b/apps/meteor/ee/client/views/admin/deviceManagement/DeviceManagementAdminRoute.tsx @@ -1,14 +1,55 @@ -import { usePermission } from '@rocket.chat/ui-contexts'; +import { usePermission, useRouter, useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; +import GenericUpsellModal from '../../../../../client/components/GenericUpsellModal'; +import PageSkeleton from '../../../../../client/components/PageSkeleton'; +import { useIsEnterprise } from '../../../../../client/hooks/useIsEnterprise'; import NotAuthorizedPage from '../../../../../client/views/notAuthorized/NotAuthorizedPage'; +import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; import DeviceManagementAdminPage from './DeviceManagementAdminPage'; const DeviceManagementAdminRoute = (): ReactElement => { + const t = useTranslation(); const canViewDeviceManagement = usePermission('view-device-management'); - if (!canViewDeviceManagement) { + const { data } = useIsEnterprise(); + const hasDeviceManagement = useHasLicenseModule('engagement-dashboard'); + const isUpsell = !data?.isEnterprise || !hasDeviceManagement; + + const router = useRouter(); + + const setModal = useSetModal(); + const [isModalOpen, setIsModalOpen] = useState(false); + + const handleOpenModal = useCallback(() => { + setModal( + <GenericUpsellModal + title={t('Device_Management')} + img='images/device-management.png' + subtitle={t('Ensure_secure_workspace_access')} + description={t('Manage_which_devices')} + onCloseEffect={() => setIsModalOpen(false)} + />, + ); + setIsModalOpen(true); + }, [setModal, t]); + + useEffect(() => { + if (isUpsell) { + handleOpenModal(); + } + + return () => { + setModal(null); + }; + }, [handleOpenModal, isUpsell, router, setModal]); + + if (isModalOpen) { + return <PageSkeleton />; + } + + if (!canViewDeviceManagement || !hasDeviceManagement) { return <NotAuthorizedPage />; } diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardPage.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardPage.tsx index b89dcee32315..2be8b4c6d020 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardPage.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardPage.tsx @@ -34,7 +34,7 @@ const EngagementDashboardPage = ({ tab = 'users', onSelectTab }: EngagementDashb return ( <Page background='tint'> - <Page.Header title={t('Engagement_Dashboard')}> + <Page.Header title={t('Engagement')}> <Select options={timezoneOptions} value={timezoneId} onChange={handleTimezoneChange} /> </Page.Header> <Tabs> diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx index 5423a7aa10ab..03bdf15f0117 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/EngagementDashboardRoute.tsx @@ -1,48 +1,75 @@ -import { usePermission, useRouter, useRouteParameter } from '@rocket.chat/ui-contexts'; +import { usePermission, useRouteParameter, useRouter, useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; +import GenericUpsellModal from '../../../../../client/components/GenericUpsellModal'; +import PageSkeleton from '../../../../../client/components/PageSkeleton'; import { useEndpointAction } from '../../../../../client/hooks/useEndpointAction'; +import { useIsEnterprise } from '../../../../../client/hooks/useIsEnterprise'; import NotAuthorizedPage from '../../../../../client/views/notAuthorized/NotAuthorizedPage'; +import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; import EngagementDashboardPage from './EngagementDashboardPage'; const isValidTab = (tab: string | undefined): tab is 'users' | 'messages' | 'channels' => typeof tab === 'string' && ['users', 'messages', 'channels'].includes(tab); const EngagementDashboardRoute = (): ReactElement | null => { + const t = useTranslation(); const canViewEngagementDashboard = usePermission('view-engagement-dashboard'); + + const { data } = useIsEnterprise(); + const hasEngagementDashboard = useHasLicenseModule('engagement-dashboard'); + const isUpsell = !data?.isEnterprise || !hasEngagementDashboard; + const router = useRouter(); const tab = useRouteParameter('tab'); - useEffect( - () => - router.subscribeToRouteChange(() => { - if (router.getRouteName() !== 'engagement-dashboard') { - return; - } - - const { tab } = router.getRouteParameters(); - - if (!isValidTab(tab)) { - router.navigate( - { - pattern: '/admin/engagement-dashboard/:tab?', - params: { tab: 'users' }, - }, - { replace: true }, - ); - } - }), - [router], - ); + const setModal = useSetModal(); + const [isModalOpen, setIsModalOpen] = useState(false); + + const handleOpenModal = useCallback(() => { + setModal( + <GenericUpsellModal + title={t('Engagement_Dashboard')} + img='images/engagement.png' + subtitle={t('Analyze_practical_usage')} + description={t('Enrich_your_workspace')} + onCloseEffect={() => setIsModalOpen(false)} + />, + ); + setIsModalOpen(true); + }, [setModal, t]); + + useEffect(() => { + if (isUpsell) { + handleOpenModal(); + return; + } + + const { tab } = router.getRouteParameters(); + + if (!isValidTab(tab)) { + router.navigate( + { + pattern: '/admin/engagement/:tab?', + params: { tab: 'users' }, + }, + { replace: true }, + ); + } + + return () => { + setModal(null); + }; + }, [handleOpenModal, isUpsell, router, setModal]); const eventStats = useEndpointAction('POST', '/v1/statistics.telemetry'); - if (!isValidTab(tab)) { - return null; + if (isModalOpen) { + return <PageSkeleton />; } - if (!canViewEngagementDashboard) { + if (!canViewEngagementDashboard || !hasEngagementDashboard) { return <NotAuthorizedPage />; } @@ -51,10 +78,10 @@ const EngagementDashboardRoute = (): ReactElement | null => { }); return ( <EngagementDashboardPage - tab={tab} + tab={tab as 'users' | 'messages' | 'channels'} onSelectTab={(tab) => router.navigate({ - pattern: '/admin/engagement-dashboard/:tab?', + pattern: '/admin/engagement/:tab?', params: { tab }, }) } diff --git a/apps/meteor/ee/server/lib/deviceManagement/startup.ts b/apps/meteor/ee/server/lib/deviceManagement/startup.ts index 98a975b5d8c8..963f479f5675 100644 --- a/apps/meteor/ee/server/lib/deviceManagement/startup.ts +++ b/apps/meteor/ee/server/lib/deviceManagement/startup.ts @@ -4,7 +4,6 @@ import { settingsRegistry } from '../../../../app/settings/server/index'; export const createPermissions = async (): Promise<void> => { await Promise.all([ - Permissions.create('view-device-management', ['admin']), Permissions.create('logout-device-management', ['admin']), Permissions.create('block-ip-device-management', ['admin']), ]); diff --git a/apps/meteor/ee/server/lib/engagementDashboard/startup.ts b/apps/meteor/ee/server/lib/engagementDashboard/startup.ts index bc93dfbd9316..e523a1e42cf5 100644 --- a/apps/meteor/ee/server/lib/engagementDashboard/startup.ts +++ b/apps/meteor/ee/server/lib/engagementDashboard/startup.ts @@ -1,5 +1,3 @@ -import { Permissions } from '@rocket.chat/models'; - import { fillFirstDaysOfMessagesIfNeeded, handleMessagesDeleted, handleMessagesSent } from './messages'; import { fillFirstDaysOfUsersIfNeeded, handleUserCreated } from './users'; import { callbacks } from '../../../../lib/callbacks'; @@ -20,7 +18,3 @@ export const prepareAnalytics = async (): Promise<void> => { const now = new Date(); await Promise.all([fillFirstDaysOfUsersIfNeeded(now), fillFirstDaysOfMessagesIfNeeded(now)]); }; - -export const prepareAuthorization = async (): Promise<void> => { - void Permissions.create('view-engagement-dashboard', ['admin']); -}; diff --git a/apps/meteor/ee/server/startup/engagementDashboard.ts b/apps/meteor/ee/server/startup/engagementDashboard.ts index 16b33d27c8c1..2fc393379bf3 100644 --- a/apps/meteor/ee/server/startup/engagementDashboard.ts +++ b/apps/meteor/ee/server/startup/engagementDashboard.ts @@ -5,8 +5,7 @@ import { onToggledFeature } from '../../app/license/server/license'; onToggledFeature('engagement-dashboard', { up: () => Meteor.startup(async () => { - const { prepareAnalytics, prepareAuthorization, attachCallbacks } = await import('../lib/engagementDashboard/startup'); - await prepareAuthorization(); + const { prepareAnalytics, attachCallbacks } = await import('../lib/engagementDashboard/startup'); await prepareAnalytics(); attachCallbacks(); await import('../api/engagementDashboard'); diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 0140332a73b5..8d506c26f19f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -13,6 +13,7 @@ "set__username__as__role_": "set {{username}} as {{role}}", "This_room_encryption_has_been_enabled_by__username_": "This room's encryption has been enabled by {{username}}", "This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}", + "Third_party_login": "Third-party login", "Enabled_E2E_Encryption_for_this_room": "enabled E2E Encryption for this room", "disabled": "disabled", "Disabled_E2E_Encryption_for_this_room": "disabled E2E Encryption for this room", @@ -408,6 +409,7 @@ "Analytics_features_users_Description": "Tracks custom events related to actions related to users (password reset times, profile picture change, etc).", "Analytics_Google": "Google Analytics", "Analytics_Google_id": "Tracking ID", + "Analyze_practical_usage": "Analyze practical usage statistics about users, messages and channels", "and": "and", "And_more": "And {{length}} more", "Animals_and_Nature": "Animals & Nature", @@ -1461,7 +1463,6 @@ "Custom_Sound_Has_Been_Deleted": "The custom sound has been deleted.", "Custom_Sound_Info": "Custom Sound Info", "Custom_Sound_Saved_Successfully": "Custom sound saved successfully", - "Custom_Sounds": "Custom Sounds", "Custom_Status": "Custom Status", "Custom_Translations": "Custom Translations", "Custom_Translations_Description": "Should be a valid JSON where keys are languages containing a dictionary of key and translations. Example: `{\"en\": {\"Channels\": \"Rooms\"},\"pt\": {\"Channels\": \"Salas\"}}`", @@ -1807,7 +1808,7 @@ "Email_from": "From", "Email_Header_Description": "You may use the following placeholders: \n - `[Site_Name]` and `[Site_URL]` for the Application Name and URL respectively. ", "Email_Inbox": "Email Inbox", - "Email_Inboxes": "Email Inboxes", + "Email_Inboxes": "Email inboxes", "Email_Inbox_has_been_added": "Email Inbox has been added", "Email_Inbox_has_been_removed": "Email Inbox has been removed", "Email_Notification_Mode": "Offline Email Notifications", @@ -1865,7 +1866,10 @@ "Export": "Export", "End_Call": "End Call", "End_OTR": "End OTR", - "Engagement_Dashboard": "Engagement Dashboard", + "Engagement": "Engagement", + "Engagement_Dashboard": "Engagement dashboard", + "Enrich_your_workspace": "Enrich your workspace perspective with the engagement dashboard. Analyze practical usage statistics about your users, messages and channels. Included with Rocket.Chat Enterprise.", + "Ensure_secure_workspace_access": "Ensure secure workspace access", "Enter": "Enter", "Enter_a_custom_message": "Enter a custom message", "Enter_a_department_name": "Enter a department name", @@ -2159,7 +2163,6 @@ "Federation_Changes_needed": "Changes needed on your Server the Domain Name, Target and Port.", "Federation_Channels_Will_Be_Replicated": "Those channels are going to be replicated to the remote server, without the message history.", "Federation_Configure_DNS": "Configure DNS", - "Federation_Dashboard": "Federation Dashboard", "Federation_Description": "Federation allows an unlimited number of workspaces to communicate with each other.", "Federation_Discovery_method": "Discovery Method", "Federation_Discovery_method_details": "You can use the hub or a DNS record (SRV and a TXT entry). Learn more", @@ -3275,6 +3278,7 @@ "Managers": "Managers", "Manage_server_list": "Manage server list", "Manage_servers": "Manage servers", + "Manage_which_devices": "Manage which devices are connecting to this workspace to help ensure security. Information such as device ID, login data is included as is the ability to log out devices remotely.", "Management_Server": "Asterisk Manager Interface (AMI)", "Managing_assets": "Managing assets", "Managing_integrations": "Managing integrations", @@ -3511,7 +3515,7 @@ "mobile-upload-file": "Allow file upload on mobile devices", "mobile-upload-file_description": "Permission to allow file upload on mobile devices", "Mobile_Push_Notifications_Default_Alert": "Push Notifications Default Alert", - "Moderation_Console": "Moderation console", + "Moderation": "Moderation", "Moderation_View_reports": "View reports", "Moderation_Go_to_message": "Go to message", "Moderation_Delete_message": "Delete message", @@ -3717,9 +3721,7 @@ "Number_of_users_autocomplete_suggestions": "Number of users' autocomplete suggestions", "OAuth": "OAuth", "OAuth_Description": "Configure authentication methods beyond just username and password.", - "OAuth Apps": "OAuth Apps", "OAuth_Application": "OAuth Application", - "OAuth_Applications": "OAuth Applications", "Objects": "Objects", "Off": "Off", "Off_the_record_conversation": "Off-the-Record Conversation", @@ -4720,6 +4722,7 @@ "Service_level_agreements": "Service level agreements", "Sort_by_activity": "Sort by Activity", "Sound": "Sound", + "Sounds": "Sounds", "Sound_File_mp3": "Sound File (mp3)", "Sound File": "Sound File", "Source": "Source", @@ -4733,6 +4736,7 @@ "Start": "Start", "Start_a_call": "Start a call", "Start_a_call_with": "Start a call with", + "Start_a_free_trial": "Start a free trial", "Start_audio_call": "Start audio call", "Start_call": "Start call", "Start_Chat": "Start Chat", @@ -5882,7 +5886,7 @@ "Service_status": "Service status", "More_about_Enterprise_Edition": "More about Enterprise Edition", "Presence_service_cap": "Presence service cap", - "User_Status": "User Status", + "User_Status": "User status", "Active_connections": "Active connections", "Presence_service": "Presence service", "Presence_broadcast_disabled": "Presence broadcast disabled internally", diff --git a/apps/meteor/public/images/device-management.png b/apps/meteor/public/images/device-management.png new file mode 100644 index 0000000000000000000000000000000000000000..e696c23eb07395bb6b48524cba1e09dc78343662 GIT binary patch literal 40365 zcmc$EWmj8W*ELSD;_mLn-L1I02Df4bij)L*cPUof-Mz(~;ts`Kg9mx(bwB^%{g9D! z&Nvy_doP=F%@v`hB8!GhgbW1*g(fd2tpNoEV+sWY4fO%x{RxieDE|8wl8c<a8x#}@ z_P-A_)Q>EJ_lMAK8nTj5)srMg?>BHZ63P-#P<3%A&t~vYP}!35(h^!;&}UsJS!DAm z<X+|E$4+@pg{i@0tQaszgwUL(4(#Z#9-$n*>|DMycZo{9mP2A5Ng2xZ8SA-cFUlnJ z*v{MDG&>`ot}GFzPBDC7_+c%gfAelz1&GV(!63B}V@RqNo~O1Ko$K`2U%Y8$mmA)9 z+=@Kif^wgpZ@*~IgEq?zzrAe=9OMwAqUIpU=czH^AcshD*w|8pNd9*-kR^$f{(qMw zQvd%hmr$uc!2I}l`n-on>kY%y<13Gc!`Pswu6blr<PAJ3@PL<@O$IECi`KYSkFl1l z346a>7p3s~3FB6kXnB?G>(2BXi6*(8FnH-?v^p3-4pTKxzcZaFOfioYT=*4P&A&lv zN}U;?nX&g!JqnTf*xORwNcN^a-cx3lc!@X(m?hMs&?iugBrV2{jMP$$FHSnNJ*H=6 zjmLnSI+nWJr$Du<mInzgaJVO$0%vZ0XQJ&>BIt>OizY|1CV<xNKpzb~ziU21yc;0} z#f^5*V)tvxq4|i<-&B1kpn%=_jqB#F{^Qe^O=@r6qCx-ew}HHi9`af5H*oIHe6pW# z<FK1Ug;{B{_v-b4%1(6mwo8ySROgLTCYX-NXNGYBuO+tg4nha}7L14ISfEO^B_(jr z^71Qm!)9Bh^PEOQX-2)=moK{pa2}?=eHPbZ{INGmrSdE3xyYRYitG8;GMmHVh70Yw zS8dM0h9rwUtA~|bHd<8VjWPMDCA{4P9>B}f>j^|m_o9cjZ(LdP^Bc~509Gxs`YCmC z8iK8@?Qs99EMtilq0>_*dPjEiQrL8M7KmI0-$T$Ra3qstSMZ|9bN0zJZ3~j#w&-LC z6ZLcH+jd5>Ve$Hg`Mq&<k;6THopH~Fh4Cq2+vR$VId++0k6~QRtMF1%x(JeL(@VGK zX!qiIa4KKhzA|v&gN4cWO`5Wcp6<8XyVs<!zil85EkuQ1EUn}>!n>d)+8W5$#<6jq zt+8wepkwXW6=?WOaP7tk<ZTDKECPcb)`gumIw9v@(ufbn04%x2Ef?MU$`syq*L3ln zBV!_L3wVHx0XjR+&WF*vkhC9%pqJrHOGQl57RPQuAa?u3%P6%*H)m8?&#CKKXVLL+ zh3=*w=<UnVOUu~T&9}~GrhNo(tUoT~!yd?eEg<(k&*fwoBG;NEQHS516_<ChbKVW~ z#tt;}qrKe+Nk?`qd9!t2P%~)+nUm{53aHJR8!6GdIY9?SbnY!v1Lyi1Lt5v#&-S10 z9M<A0g{~lUZV=H&x`58R2hqRhZE=4eN55UDmU*=9^n0qVb+<b<^o?dPfHxA%FM1%I zz{mCao0r9VKi&<$j!KHK@q+|$&uHd-1ZD22zaKeKpE^gV*~ae^3t_u1W!w-!b@;;0 z`7UCdd%gDwU{LfK(&Bvrizn7))1q#u^EOk;RN9cr4&>ngB!1?*`VIfYaKrLUMZAda zZ)baC)25?a9eR_GDsV4l^kl!~q#2@LdC^^$yg1qEvAoy8aNyl`_7#jvE#&di?en+T z?SxIIxclwt6>`j&{c-E@eZ3^>Pf=ay{K$2cmeGvGvEJ0sZU6+Ip9dg{wa)Ie0(R9a zfOt20fME%w?pC?iN$C1`3_|emWH7mV>g#a*WZJSWOK^SN>Zo#p6s7Xl2?DahgTzDL z&k1pUQHQS*=bCP*)91W`xyHnk1)unfhw(DRNZ6T7-6dC_449-});O>Ya!j)@5xd@a z>l8d|9N19a?D(NugZ?iBE{YkW(G?Q;A;L9Ao9S5BRw%S`y84q$fdRj{z}p`oCj5X- zwzYfC17)3oZD6fm9gfh7qg!88ip7xPMyGJMN1NGs*Jx%u=-Y17x~@$fx08VPeQB15 z@?Lw^NZ}Iy2(gd16}}>%674;dY_=3-rQ3GsFS^t0(~Nb>>a<ldG^;iz07qV^dZWha z#d<let3Urmf!pnCT2Lo1vo*p2KeW{1DCFBAvFV-)0v8YFZphT!ki>R?t0=`@vokOI z?6=e(me-<lDGO!?Y%Nt>*-lL9=LFS-(aT=43PsE<&qo^;pf?+Yr`NRHNym;jTbada zbVPvA`tzsL%Vpp>810}Hc?Ng$0G$Yx;c+Z#D#$V34&v}N&YQfa^Ts3dw7KnUT*wnb z$EH#i!BO+t*}T`(H({~3^~FNK4)_h{i=qGsx}U?Q4x)*?*GSI>DC6Gi@32!y`O-4W zi^|qy{zq5$=IhkPl*S|QZ0w7hiT62|+eL>n=hgfX7_9A(wcn(_`R0)A)Y|>_3M`tz ziDD~dyC;N~0lP(V%IBC{yQpBY=N|G1eR#RUa58yQOlNCPxfJf<bTziuxK6ov{gL2d zb$heynpiN;_bhW8W(8V11-hD?+WP%HyL5#R4(+RiI%VX|^4)hIG)=J%oqef`D9;+9 ztEqez#a9s>m`Q$Wg=|1?PIozX``9cV1V#To%qVIW?B<N5R6X8S+-WZRn;|$g#aFhk z%)S@P$EWh2iM*{Q<{?vyIb1EMEmhqR%SD@#`)uaoP`60`M>;}*eGthSx?w!EFD=uH zMthI%nEpprJ5W-*&v`e4(~rRGO3{*U$$-!cnJEbW<KN7=>*I(SJm1bWch?-<RG-G& zFLho5X*T<@n46Y=RbZS}ujy0)hTs+3p~u+sHa6!ryHkF^W;pUiHY65vFA4A7_W%=j zv*FW1v66tmRQcNdrikb2kC?FySkPO;pZlJb3%?^9?c>K($m_o~&Y;O`4C$yjVAUe# z)vTHCwX*%_DqPepVFMV1y~(iLnYUsqZ-nuTs<x2}0&;1yoma5|8=dmwB2V<Zet@W= zk&e5Ry&#bYybkhn#H6g(Hqf|Zn0DxUnz`s8)v*V5Jg%{KkQS*qY%}uuDuQlaEJXeC z$9=+pFB2X29tM!~#b%B8)2CPb{rP)8kIlDM?tD&%EYOASMg!BgIK>z?87uyh59!Xe zD_h;%-4Zlxd2Bi0t$wan#v$3L81p3UX5-g)P|#EZ!mh~_L!FJQT}2QAY`|s6!T`xJ zTHd@1e<@#}+;3J~)6FP|Z4b~P0t7ouTC(ByQXZ8Q2XHq4Ojv1~)f!VHA<o;ET|NWx zi=Hj9<~zUAHa&aB-s|*+EP&9C>{Tr3^S?rHaQs5g)Y)_1A+H0f&aLUZ=V@8j=`?@h zKUi0LK+vnhALXkZ%^n_=*ch@tcJYI^swucr#!|Opi*|Np67lYEWUM(oD$XQYgV0P- zGpvwJ9@%M<Mf@IOV@agzsXpvDLcdp?8K8S{T@^M~>QcIqKmGA+A;>D!R$!HqmF@6+ z&%>7c)|~<ftx*c-e&b4VvM*h{7h`ym>Qk}llQjaLOulXHP-jM#6;wO^nn|MzoOn6| zI6HZfja;McYV@RAN)AJCtri^t2Lm<`-B_jp<(=7Oh~L@n?d*%Tai!|=>%+Y4z%f&( z`F%IVq}v;K=JVI?(}4)VYEbB$naX<B0+C;vqvtg=yZ!x;>{&kt7&WU(-C?O&zWeEV z&`E#MO7x=hYckbvbO8%YN!N01>)e`87MiLg7Z1)Cz*YkCb?DP;Quo_(yUoE1x`^GF zPtV>mq%g~=yOYBdfS<^rp%vb)BVkU`y6wE#X0!_H;SS6ioXB<l$y^*;oig?xl)Z%? z55dO9cK2{+;piKo-sjcm{Tzw>Rb|P)t>+YIcK3^rNWieUhWbCOJNb>9+f?e3&fdYH zFmEdX70Ip0rWr?RK3;Q3U@Md=4*1w#VKX$C3}pNVk+-13^$bs7M5i=UR{JZAQtI<D z4zkGEb^RQ<JcDE6Vv_!cnZ+pJ!AQ*x9h|wWdQEC7#G_3%m5pmhKR+R*kM*y+8Cu$J z8U-se8S4JS-T83Wx~gN-vcvmW-QL}L2ASN*^tF&3!^VSDp`@2eA(;J+t#jp3iz+52 z?f>5AR+uQLCe@>L;w@hJNG`$DWYD13-dPVk7X{jttZuebn|>N`jX4{;ipVb|d)<4x z!hPype~W)3dwShW&b}BSe@h0B<NxeIL?@+tKZ8;P_OQ{{V(&!u+!p8&Ax*FoN0Uw7 zXnfY?wQ)|XbRv&+>W%@x@6<ynB$~(fks0tpG>-)IRT^z;b{;QX@t<H4#LL#@em&{A z_nSQsg?w>8k##EedxQA(n=Kl>_|g%pzV4RHO;7(e^MfqyG+Ha;lZs7`Z7mwP@c;nd zM`+~@OdqeUfN*Bs;IkG1lUkb#;+uU#XYkeOVJYJY+}ZQvR1!CvlTU^T@W`sVp`aie ziE<JEJfgL?v)g0moA~vYq6N)szwMw+nSt2seOC%AVS?-5)7St`qmGOW*pAM2j>=DQ zg0EX5fNdb(+9l;=!8_w|i%<ujxeCZ^kT_Tr52vvPxN3(uWkx2D3yn?^wbfmhjOF03 zgtSSsIs%T-YqEUQbe$N0RD;4L<<(y9bcAi@-<RAUA%~$SV#CrqQ$64`_aU|yQtI`m zZ%=DBzA@dGLvQiEZ<}vIPftCM2vtYN<{IEXeQ!bFt=6OKu_Nr<S01Tt-;`M(q+2Ya zNG2?O{Q2?4Pkz(qFB`u|FZq*i^1*;3TgG?M!CjlF9KKexx2w3f2D_oKhll4`G3z6e zQ5m9sH=)-vKK@Pm*NK|x``1-~%SsR;I>(jd<M4e*&s$vnD=Mn%a=4UeatwFR*C{df zp%Ntl@;%YlvA5Yda0Z}=?YYEHPVD*jeMYiJV++@#<m2Y6U(djqtoOUOtCLCx_a1zw zV%a8lXa_rNzzDVgdlc$i@S!0Fv1NthevWChu{9_~UqsE~6ptgpvF&%T`;kIXA`G;E zCPYdC3LSn#c_IRWzdO!r;FHf(H`sLC@##oZSa+gN>ovX^uglJFnjaxEU2n+?MoTj8 z?1|r(p(0@O+Pq%qo@{z<RS)y3d(2=$IGtO!f{LHJfUM#_@N4`8s6f`k{y<7HOH4<& zJec27#Jq10-|2kA#g>``sZ5Js{q9!Zf_I8c;;UotKC0aR=}TBife7;d+Jo+a_<@!& zTISusRXzRQLX#JN+a<=|W6=CO!P)Q(jJ`kFHNfk1zpyXY?$Fw-H!#NN!8*Bw5<#@* zAhmZPDR=HfBFbpY_)!BNZk(snO_%FII^LU^3}=2Qr^_b;DE<U5k3l0wK}x|(K@(3A zFb+KOdn^IsFyi~W4<quBC)Fuq>PN__(i|}%{<CH<tu@70?`A6sMTV*BTv|XV0hzE@ ze2t9N6Xb1+Scz=fB%YI)H$pNyK2@SUlrzsfcy0_ax#Zn>=L1!LQuZ^c;xhPYW;=uT zT@4NP(u~@O>K(oVx*Z7fO2)OSY)jDlC`3ay?$TE8+bK-<BKf+o*lh-a^4p;TusC6_ z;kV)Zlh=W(n`RO77jI#HLq`<Qy*w2M<D13GX>Lmg%`Ze1ESQKXlr+j}<biW9gJMs& zg`eV!l!Ma10&?i#M7`oa76>A=3sr>2zj_drp)68(Gh{~OP^wa>P&ng!-odb&MwHu9 z6Qj{j+|*@z?>%fnKF{c4u%~)`YotGdzWEn5`0hiSj|2zrnJuzSnn0u4g*p<wt2h2= zp3oq8gwB27y7jRzyMJv&TdWwwjM~x{Y${{c++R`q%I)|U_jQ+&fIwIiT-k4I7r!V0 z3D`TTQbk8gGQV@FzJN!XLCA-PK`8KE@_$d>18oR>Xrl{MZQxqAPJ%V>uG(ig$Ml?% znR1vB0<Qed?=y`a=@%=_&<(2zIOI5?Q4>aO`>Nk8U{mj18V(-qw4RF6k-`&1wPM~Q zRH*RX1_zuD*L$;{A3HxLD|XU!8CqLd!1D0$YG}G)^C)SS>mXxeCv_q{vR`1HeQ5~R zCyq5dmUURC8QvkTR%6f?&_~B&4;Z5Z-_-cOeHMq|m5f~OYx@MP`F;|QP`WS(4{(&O zXYDcZH=;;tIobHnu}v(t#rylm!=ayLhrFn-T{v;0aCyZTK18jk3T!=U)V3%3KkAq) zT{(T1=Q1u3|F695Q|q<hJc*#S4maR4!!!0&RfP}{mmiS(22lDfA-b8m3^&@%MBr|i ztrjj1J_P6v*!Atverv;3Kjf%PLtMwOObbl?g8I+nkVB|Yit!}WBs-Fcw$iAKy`zo| z4Dhs4fJi@Ijau2i`JNS^C2Wc1T1^K0x`L&X)%_3*ZeNca7D+o9q52%e^lxMD#ZE~N zu2&RBUs>x4t#Wqz`3F-e89w)7tmH@gKgGp^!A2triX;UCQ2MiU5TlvL2*cE*Ib7&5 zi>2OAw9285<#USv`csjSlrwA+Ido-zc%%-qOU_(5XV|2??9otKl{+hE5?n(FBk*%W zsw<-ZFeABL$Qxs@b4)T1Rz|ow^vV6#-;yc1%y@2f%DZmV5XCL(X!A4|)PgOS#g88@ zB4h2(&?9nI9O~CiXMG$UgfuySllXO=+G9P{fbh6i+4<jDNN&mIsCPLgvu#yF_0zKk z;e&EBk(Z3;iKGv?GUCVM-=KL%Mtf6%%iW`{S~XfWwfdeM=KB^0_OQkOelO$AAp7#+ z0k0!se^l4xtD_DpW#h0;`ck0Gq8(7@*s6kB%2hk%&f6H19YTrkCX-;}RfEO;pG6h= z@N|=@$1evu?v|ZQTGkAt6iPKhQ<~sWlHB;eqOrt1*u54uq6SoSXvce7mTP$pG64R! zXLa_f%fu3PT^{-5nv843E*PpGZQcST#08h*7!x?b;%0l)Yk>nIwcHMNomKYHy$;_j z=Twbk{nVw`1<X_2goN3;lQ*&cTLexBM*iPSLvRS;tD`rJjjBvXY?;a!g`tF7<9AMB zk3hOj5y>PUpNICXnW*ti17sZ*W;sK3Qk_*Zlm!g9jJwFIE`GC^bHZ@__@m5VeOmC_ zH}PM+<4iCd(PN2aEJ%uRE=yawvP|VcTU(e!L<?xsxbMDW;NNn8Cv$Mcfi7xp+2IWX z1b-EP!JuT;qy;3cdd^BKHFsV)ll>JVrm%|P^Yv$vnOo57B2a8+piHK;!iS=>K|)d- zqi|)EBgLqloA>LsflyGtD()n^%o7dL)7jb`U}^lb%`Lmv6KYbe>tAHPQ<s3LyA|HI z5oFook{D#!AzLR5155R}8ff@kxmE^+WQER|=fuDo$@m4WCVf8NUy+2XX?Rf!-F3PD zA;G2(<aSK=?SInr?&Nq*t29?8cuglq2gi@)MpyjWwEFNclvRq&z8!>0{-xIJhF1G1 z@L!ZjCd8S;py$3cwf?*;YM_;FV!@9H*KNO^7A=A4#teySpP^0lNVlvamaX(xES2Wm z7q#7tluhydhf&Q%f7ZOwpv6W1x%o?NFi>RH9923FBXF%4YK>;`Sw=vrf>QH3#G+gQ zm2o^_8y;Rm)MtREB;46<y_5nz1OY_pv)E*o)R6IqkJ%c(WGX99*RyMqJjV6U;->05 zCdgyxpPh0l3JQz;Q@!qEyE-OhJ9?><X;%1!S)it!PAbXlI8nl(q}U=DfMZQY`1TrS z`@Cn&t%-4M`8L4eQO@s}r!_@uqVajXH4p0fGq4`(cTHd;;p5u0vkdK_x0{%;yBR*K ztrPEo^K9wkZT)NFx0@$oi0x4#t^3mrp+Tc9<(7U>A*-M>C8>x<7CEGNjO{-pFI_R- z(XKNcs<?P{J3HlAl;K~ck^Ch<h%K#tqPa+lOQv@)*EfLZQ&&0>UeokL($v-3pVmhZ zw{ftUocDranPJ2*yBKFR{$#@7>f`!+{I@3j!WS2iqG8mT46ecyE>GC){s18<sTHH1 zGYh<s%Oyq=n}KEcf{)Vhr*;>#BsNN;%neV%Fy?=mav-lyb|&g5o>+F27Q(;J8rTqb zXT(axwEE>a`n)R!Hvv>)+Z$gS3|#+;0-qgaM;EV(LCuj`m5(Dy_!0vDXpF4k$t0KN zVnt)5LX=ip##GNcz37=pYmmJkqTf#Q-A)iccPg<ah56IZNq&dlfz~Sx_xPWQ@L01` zUH%0qMpIMMkBe=rRNxl6hiQDIGVa~8{`f?2=7I>GeMx=!aOCQ|x9@ke{g6Q+QOSVm zXdG&QM8+q+`m^Zh;}Er?7v*PF{-dOZ=)wwiQAy)c3ec;pzi>*ySr@M=YI!NC_)EAL zes*jRyIj{A@Sj)6)jf`maV@SpcW0N<Fo%p6F{(2e9qwas`H+RKU}3TI17m1x^@U2y z$|-_q4{U1iSuL>;IPq96eLC@L9#SM}jP%1QB=)-tgXdHkz~OrD-oelmP*M>z8Ts-* z?q&XJR=yj>>9KG$ovo^wSyC%biZqk*?Pm@i+Q^I+H0S(Z45s*6qw6Nu$K($LY!vC^ z)uz$C4WYLlZ&*%P)@SRLYa{yW80=~8j|dVbt>t9z8Reb)K#_sTbTO!g!qwp0oERMB ze`M2l=a>D2UPWeQ=<1=C)87fJDJtvb$@et-Lc@i{t_ZJxh|eNe!z^0~|M?bgEzB`& z<l}kdv4y#L;Jf7|)gdk~PVKw^@8EIS{(i$`R@@)44scjCbW1*0$d6gv6o4KrVP!%d z?;5VPxqJer9)~h=0kWHuK{Ocw57TBMjq9bY@`y@N@n7E|b8%_!w$ws-_{b*OQ(!5W zyVaG|VJ#+J2k#P6EJ3$!DL1=ZPuOfqp_gO`$Q!Ys=lqKH`E1FQ1~5wv5bgM+E^kdb zXI<4_FU{prXojUb3s;%^RQu<=BEHIIRy8rd{ZCBmzr4EfmnhFBge0(2B-5BQxvDrb z7AHE&6c$Lmw{nZ1{0w9Z<MDOj;_1B5_`yq}lTu0?bd7e-<(<vVqx|c7O_je4IsAuW z{|q~Bz<(rJ9V<hiMGaQlL6IsVjlJiNDYst<9xZcI_BSnl9iTQkGt!R249yHkrZ(6- z7FuPGBW80RIaRp!(0;BJFRrsY^5!%96#hT?DRO2wlMk#F@BRAQ2-gZn6eLs|=%K8+ zo{ne;Ou}O9Q5!0;M}>K-JCDBHOQ$Dgwx%gP|A#Z)^AxN^w}rMuS=$27#=U&NG1^`E z5ci*+R&j@(Q~r)DK?>6l>9KjSw7%YgFHQdkPH2RQ=-ZkT@DCJ5IQwama_z}7c<BOb zD6n@GFR(a-@T*V!{c!=e4E{sLxvkvJw}t;D`ACx8@r>;S#%xWg8a$>GOtIn8O}gx| z@2>TCO2M@nbH-f+*_N}v^D03Vz=MneTmTrZG98&i6=q+thNe>Vmrt6$ALwd-ZO?R! zYf7^zfOl1voHKK8-_+%j+tF`NyBzNna_)l=7DrM9>N(6a2x)9va;pvSi4mR08nx?S zQ!cx=9-Ez-Hp<fzy53gkg19}J>^2Q_U+i^)y?>>zUc2A+7@7o^iLovA6tj*X2eQOm zD)u`NpF0>B#l^&ER+(8^R*E;KV3k#}u^VO|Ysx(J`VX@C?NtB@1OuFpDn$5!g7)|p zJ5BdLr)b@k%=$E;d1F8i2}8DTLWpHGHMt*21trf&qYg>k*4NgQM4Y>ohc<*Hd^Qhx zhjto^zi21olh@5x=+3;pAU^1^p86daue>~&tlV9H^k@s{j?Ux=<Gs&}d+?{!v4!-8 zRL#BY`_9%BKP-(CA0d16G>1U~&~NICvbod5S3SF$(>yxc$Ap6l_Al7|jQc}A3{#%F zIXjcLR25l4&l9o7@&ei-e6QykxXlM~&xOw4e)q{fdYh8S`-#&`Bd&pC(yB@fegnjA zKHNj^n5@KH1#=)^_56uW%k8>!o$0}pf?}VHuWq@Zxt}K0rC>W*sLD?I8shm7rYbsM zVW+%k@3z+G*sphda&h4Mmk5+}Z#n=!_L%XNe4=%=8AQJc3!O7xZ<P(qin<35O&Z@k zN3p2`oc(6-I?4TBE~(wYVPgFUVjI^iRorL3S9{r62k}Sf8<>!cJik_S0ylpAzurH| z3knM6HoC*@k!)m#62UOUH4L?-uk2IY!)drj2BVkuVrhqB0=6`6*c@$<Pn#GWbH7N# zpC}T$eo<OUu-uN}BX4!?Bc(eWA}yhF5@N)bHatWQ{lQ?r?HA2V{|HS?Lb40^(Q{K5 zcUc(dMrkN@6-Ja~Yja?}QM!7^7&;OxHf$}Y1(piseR(LeDJ>`n;`>d?*K>MD&M%kN z;|%NPUw{tAjP0`ok5+VK$cet3E{sbDiL$rp*P2cuO)6UFKlYQ7^T0Xq8T(9FJ2BJw zP<Gm)ue5yK`JTgXpWwe`g5vGHs<a=k;tz!ym$mnG-s)*)dSlY=YaEA%PK|-SCMZGl zZ+Jv^TUkc#KuG9#qh}+S>xiSUXz>Oy*y@6~=V#QSiRIvG2QVhWH_qFSH={1?S10-5 zfsxX~gG9*khqL5+0<QrKNO*eZ*agf6Y$sRx%H?Z#u-D6z;=`IR+hygY$<H&lt4cMo zL6oOJjs~Z*kegP@6pE&cJh5Uw0z--pW;VUSU6dCIqUR-9QDH8UD<hpsy_{&F^SGnF z_9!-m$uCG0rrjyAyg;}<whs}gB{M&xanm0&cP%tEspnxPQDf4bZ(UfnqI%O^c;M=Y zBN;Ggn9p?MF~(43DM#a*evQ5-0+u+5r{@?Po3-gSg<sbAvy`Tej*icDy+hw$p1uih z{j~@M+&}!S((j6lz!x>A;0(vUxx1Y%h!b%Vom4z9GT((s8XMS`iito>+9Bd<tKq_? z_PzB?kFYq>kRnTCF*9G7Z{mE?UYko!I25_sDH*TX$qpr`{NX0q)~iJG?vDk}sO<XU z3IQWSbe)C(uL#=DnKdiYO%eV@Qt=w5Fvjhc97%XkJHs142`qQ>F2u|WC5P-4U=SiO ze3PS9qcxKfELnNPgvBYYSf0F$WOm?lc{@=aRy%4k`#1(&VlGJKf=n-O?7rV1#?2ls z*I91oioiX?!rqgKiC<1St+WOVD*0{r$#6TZ;5A>xRiTAC<T=jJM+@D+U$n3Lb{-h! zcu&gxPMsX_p|l@BPba6kh>Q5^Z+Ad{{y^}SJ`^X}cnh3OwHQ*p^w~5kyz`p6T2HhI z^(5X5`G#+toFLchV7|%LKGC3O=mU3pYWrDfZ?a?$i_0(^YrsnW!)mbdc87pG%6MF8 z_?#B{&U@E*yT43Nn}LQ&ZWyn9TcI1c%cS$ix>x20f{Orbkd6J+6YFP|zf_(TuxY_< zg7$plDt^|jt|DFeK=&#mm4BTiXgjRAyLf`-q)rr!JOfS737ffNF#FmsjDiuP-=RW7 z?ME6d*a_3!CxMiria>8<0lP1b9F~oO$HQA&4WYZ}wL@?YJeI4#Woc&Q0#t#9A|L%g z>$EM5Wdk}>OseUkTH8h3kSvItKCkhOe~SOar{sU_4Vf1U)4$xx-?^3FRiDZ~Qok*! z)TwNwj^$vii4Vi(Y_A)Ms20}N(i3tf5GcHZ8ZT}#w&adwdF$#$R&8hv)Z0V*PekO> zS$UW#49PTf5OXdYOx(dn%72|XH=NdO5b+lHC$#=!@C$#F+@VF^OhuC@$H&x-r1Ixo z<J1ip!)$wZ1whsel(rvW<*{SZPtkC&ymf8bnRy;p%BWW-8H!4gLTExL^)-YIN3Br2 zHS+g+{Fb%jJqW+m;U-*c8P=Kb6y@`BSklBxCjY}`>ZB~TPQ~y8I%F&30i}n}$3LJS zgDJ}7)q%?Txy6K%c<p<fFC1_BPOLO+MPRW^Ful>QvQ0b1<OI&ow^%lc<-o!ypp|}8 zp$EXjmJ+&>7-nDek2`yz9gR_h-kG@><|@uVs66-Sx)ZI{teShAw-rAr9~IWZr-Gf< z%;iSGQoeu+3C{``E;9(Z(#Em-h{hX%a_SX#6Jr#p?G-(P2>-QOnx!}#hr6{fI)6S% zivhYKs*3{bJ{6IWOa0>d2K4?9W2)`u#Vb9Ar=ojH#<SRgv2z~A)s^WuY3e5u`(H;@ zVC#2rtmO;M{Y=n~>hc0e<aKo-ahteU4gqEzGeK3Zjd7pqD(sNb<J3DUIIz~MlT5a0 zYyJm3I1{)WpY~0WrW6friBq*@i;rd>l7n%}InUb9^854>KR5SsG~>cf|Hx8u?8xWr z4X%w4S6MTK%XkWkw$~W8NDXc~2ytwNMAZIB_1|L!rSN+W6l5;OU|Wdix{upPS+C}_ znf(J(YFP~*V8Ygd;SWMQOakr&<ANVQL}jb!kaOzW{ggJaDpGOiaX?B7rceBo(-#DT zgtP41Nu&V)B$yHCTd5JLgxHUjf}pMN9g1&@S{Qv)zic3|s7|q<kKAeo(pD@<&xR1} zJpxlcT<fT}KRF2Hmh<2u4><hocHUII_dUw7-JAfeyF(mXyk>vb*+cSWP8E?2S1Gdj zlLxxvTwK1dBUW#5uB@&KkY@QQENvCY9#yMuly(2c`B>lGf&2jn7kMPIjJcpq&^Rw$ zn~!eLl#o%+b~&`@z)o*vMoW^`!z=(GlwKx;3JU(j95F^6TV^o(NhIaha-v{|1MxRA z*nMmZDy0ueiun#LICqgko=r6BS7@C@S;DD+*6Sb~F8}Fij^h(CnW5PG>*<XorMz@D zs4JMfUUIoP*iBIQIb;@c6mTIjJSjx}@;i=y-8i?Mi&8%w(ZTuk!xzBZ<i?J~cs#lx zCMC%MG2dshW86qdoXIv4Ch(WThN#iWT+Yp6w<bq1+M}7a*%aR8Ei}BVyy$G5yr8*$ zUL_hcos6S9iDmGi;6!dhqCj_7egD|)Bd+Dsv1Sw9OTM7L>XbtgX9BQIqpY}?4hylG zcZL(0R=;|8Z0PXpT@XiEl`XHX{18+!_2;TP>1MJc?6B$u@$m2p+r8H)myn3+y{WL( zj&3xO$RyW76|9s>Q3Zq9@v@M~btJL$UanJ+V6S>}vNaIGiC)EzgG-G)&v>$Q^M-#8 zukC3{JngIChj^^mwJN3Q2PQp1Q_CjOSWeJs@MhRMM9+oSf5S-pfT=huYbFn(wDOXX ztY*niZSIKwxzcR5oz9~;S%05@cOQ5Y0w3~Znfr#RF0&Gh;!}V`-82yYsW+O#wAo1) z_c*cTSJ6HA!|XDOtGRjNch!%V0=#&GqT%c7&evbv9kTFP9Td-53!#>xYON$t&Nf~I z9YI&L8=D_9nJmy{PEW$z3Ri)9xbmb+KFW8$R_~+8CZj-0*2rp)reX@G`eE{D5Y&8; zQrNE$v@!=fyRFtLHV5ix7YBCW8$c98p4}d*mCcs#Zb90IqpYB+b(6nlV4?*K)m#6T z4U2n*&gA2wLk?Ym>fC-MH6k#G_W}1QnUoFs_U?|5adCo$5VlQn^J<(zz5A$d5$v1B zx9-)YE_6qe`E`fH;<gywFWee-NxJi(s->)~jj;jgwl7Y2P^{Uq?>x=KH>9Qe_iNn4 z=T%5-DwF3s;_R$B7Vi{e;bj}zU#iz-sXhZ)Fhk!tzicI`P8Z)B56U`B#&nUjIBt$) zM%8SA1R!>cC(f6=YT;>UozwUon@b0HncoT=JxEr<VlGy<mB*%QVq@>}SLL;lqv3@Q z6z0Y+kuS<I!Q9gCrDo%!B9gA{*~{TdmW-Ktci}>~wAE;V(vQZCWNF&TK^Vhw>o6;M z`~+6*v2x-D4fafyM83EQ^ZGf8(iiP`bAC3gPr0B}5`m8!u8d08ttw)gsjQD+0gWxw zzg#tz<4z4EMW34Rnb8XtJ|D4GvAX-pB9cRUNet4JEj9iyb2|FAnDIW`7F&-twhnjz z<|{zA1+2hJ!<bQs;;iC3Y+l(HevhCpm1_c1kYBt^_}I4pD*RB^_cKH3{B`?4&v9;p zEJ35xUmOZOGmgEGIQ<YBh?9p`jw^l~2%)^kdD-I4gc!4VNXB1=XJfCBm0clVBVnGu zrIzH{B*uCnNwxRM#<D{PIpP*deUKt~rSO*g9!;Jc!_Obq-}S?8sazUDvh#Fj=scp# zoX8<u(b1FMtT=gA$D6rR{mz^C8U{C#5zz#Wj=VGb>PCdNd5yj!k$YZjNVR}=>7>*) zw(p6$VaWL`5s8cMe|QxhaR;ONQ3&vF?=bC|H=GDvJTM&S&o*%?G`J&y-V(@Xv~^9K z<TluqaUH81i$xrjaKd&RPe1-}j&*zv=FU_WSv6TPd-_V<vco&uyp@nL=lB$jHYhRk z=aYkI_haN(*@5y}uC3I!n>et@mDXGwy|02+P$YKGLP={BGC7hWn2uJH=_qnQ&2nnm zNsoexgKCs)Ryc1bTa?z-Y_NJlFjTsElU-=CNgtdblZjd!0zc9;<z7~-9WR+fL_BNV zo<9*7rf@WY;Zs{$$?gLt?W&X;=*@CTYoq(VA@5ncbY~mV&#cN_@vDpFoDy(O|Eu40 z%uSdPnVp07%DR5N+k#%T^`QuD1d42CaE$klaqg2B=aGUaMsr4O$nV0i`t;L`5%1e_ zT=}-?es3;p*m&us^Pe$I;q%P8Ux_99|KxUvg6Mm=?nJ7s*dyS6$I9QCQKyKDrF)5h zjk1`cdndU19>h1r<;1PUWAmCVGRx+flfKg3Y1U@<5=e@!RkSr0NJ1hpOAIJS979{7 z`lR|;aF2O3^3asJQLhdSCJN#8dR`O5frLii=IM?(3*lzS<w8$g#Z4*nVba$I_UPC* z=N;M3EN46~|CoJ(rBU&}9ED)KPc5L2MusoBNK@@Jn=*MlIFngmr7ZA_@18uNn~dG& z8_UIc1VmTyS>-A92dGi89!nC8F5f<p{je)6Kjp;4X$iQ??(i`8j{`2-IiRKV7`=+M zO_no)Y1NwrF-)~-tomRS&IX35jPA%s?LHWl>EV^(u<apw8*pc)FH|CPLjKeTK_ylt z%P1JsZ^K6MTM-@}F6kNh8o5G8Dfbz3;)OsEG|Y%}X@Bt9PVp$o!Y?n)4IAQa`F43e z^P^=E{p7L<)gzjoTe77wKSB)F=K5R(lL(~&KZ3Q|ez}X?Z$IQuuUbli*|Q8>lVJsa z1uJ5+WtD!5M}M?|QM)h=9F#_yr`C#9L(luz2y0JadV<3Cm0Wx-z8Z^X`IJhoP$GX} z8?3LTJ;D9c1NLeKIFBog@UwjI1<+iXbRhtB%QX0L`|AZ}*z5LV0EyHD{S>^J(r52Z z_iOS&+(JGE{6~y9ccon&RX{!52+QEdx!wF%nMXBfDXgIx9qRRvC(!yzQ-r<V+_|32 z2$neSe%#z&w+QP3^B@C^!fjYx=VDyuRE~&1LFcys&lsFTM>@`Un7SvI0oN5ft*;aU zYG=^#t^u|Fh1`6ytNY@v2y$%{(hMiCi=CUo7h^};P57?gOYikk>^9s=U(|-eDTQ|| zuyHHy#k<uIMx`L?0Q?GaRl2=E9EA=E&P^fTz^Q}_>w?dz9;^<IR=6}z&2QPB%5S>Y zx3`PNV>GyC;hFNdG#kde^rfaoIi#e@2OO_#Le6dd3}{oxBs7%-lx`s;%WXo2y2n6m zI7;8Xm&$golN`xdv>4txg>@avu?BgQ)Zo2@p5$NfcxI@a_hA=7(;bV2{B$6>1coV) zZ3HA@#64-Ew7{(1uH;2r2FBz2@GCBj`51eF^FG(AgrR!3B{90fDkGyN9u&@OQLf^l zfdG#o%5OVx#ogR5J3*g2mY8%gsPf?=MZwahqL6n<6GlBxiWXUNm}z?H{A9WZlx3_% zK^Ba{q(=7;8Xw_~3x{g&ET^v@^4-yKi69lWlv%=M#r>qd9Gcg=`3v3Va|&PXUIJ?_ zd+)6l#zaZAgrDHh>XW`@Q<G}*-+tDx)5?Hvq?VD<w0_m%#qq$E#MWMLDV9*?#p`@B zT6z_6QxU+UHAS%fqXrKJMp@Y}ODRRGy~J#Gx10yGi2KBCHCZ&mrkcw&I{WGm+J$be zXH=@(Lb=dVkikJYtOe*92j2vW%A%#>I^+R?4;V}sETt*~)g1LUQF9CRuIf4*P7Z*n zyZCql*$Q$#>D{OZT~LOIIA?Jw+QVGDw>7uuQ5s-71s|Wzl1|9e+VNL#__Bw|bXJx^ zZhuR<awJKPOtU}+t)=4i;{mWGm+>*to%2nxvns#wLPA;o7tm6s*Ax+<1q&rw&!<@) zMQ1IBP<v0FI)acqmqM4D$?-<9+o;a&hbU{`eTBcKdY47}KeZ53;&jm^m>ab@3dTrk zASMJ(j{0l8ssGu%c?WkLQbkb+S>y^LjcEu>LNdwT@;9VNB!W{;&R7F%y5v+$(xmwD zEXC9v9di0{WSM6><kfw2MR13QX-K(7OLQkhBZ+BvJc#RnwF(9Gsyr4Bj;Zc72{wWN zr<S`Q13JlS4BARpyD)Lmy#4Zf*niq(sUIo<biXCR{-kcvuy!3cOpW{a40Ah^tWfTo zu#;5Qm~5+2nT`oL@(CK}qj6a(Yk=FBtFf+p#}`-J!WXD1A!oDsVSK3D#mGJOSPxzV zTw|^f!qlACkP)bOu}M5Qt=K-ne_Vy39Bq@tDii*ZTQG4Djaf;PKvp78TK;vWO{=#s z=TH^(s0fwB;o|PSuQ->!Ij=)&lTm-x?tp8-YfO3M*2TMYUsAlT*4WH)d8~3kMB$$u zB}7HcQIA;2`l{4dFL?NY3In@?yXoyev5>VOOJLN7dVj@nwF2tQ)wm(MQ&4j|8H@St zTQkY(r!meel^RxwG~+Ns9h{2}d<kgpvF5Raa{;igG6s95Jj!2B*s=64@KKXrcC0pf zyD9BiEZaFCXlL5Qc^1?<ww2@$(f<Zi-jxcxKuNZ{Kv%8k^HKTS;-Qqn$FY$gZx2X@ z3thp?1KT}%oT=k&3+nsshNE<Q5nqr`n!Q%dG8vaa4HrR~CC6~%_x{rn@I0;SBUA++ zi`bbH2NKpz`V(34z6jEcl@v7ZNG@uq{im*%=Ra%=Rqs}~kg+Mx03X*}bej&!X&HwO zMR1)gwUb@Xo%$W$9#1>gp391vX&pZ4GfSsJX&Lt?-~VyAxNi#7p$jQnMIuh<5*bdv zp+7NX`2y6nH9907{*Ov(!KZr1sDc;{&h0+~ay01ps`Gk!M28B8C@r^7W>u(VR{?s> zCM_g(HG7m|krTG{Yn;Q!CF@QtfKTP0e<_Ik91LBdHdR)R;U>f`ISs;2!;v^uWLZZD z&@?=jbG@q=rT+Q9|3OP15?<|VGeN#Cw1Br`E^l|)`&p@kYU~WhVN{mlc%8(X=i-|J zTy__A)GqgJT+JASM3iPuexCoJM|;|u`pc2F*wlkTorLI$YqduAJDG^M{7Dbu`;_iK zk$4{S$yg0eltYs)b8}&gmR|G$4%0&v(}Tt?uo8#aP={$m0e>vwn7#3|4J9ItSy-Mr zfi}Bu9kPv8tpWB{)@wVc8#D+hB~A*%zSh38HbiH_^0W5ikwXv4g07T)Cy<-JOyWKL zuE3{SxS642ywacYS)&jNP{7Met!7f&K-{lOqaS=st`T<VNOG<QGDSQ0EOuOh@wJW# zl4jK7lxEfL$d{Nby2EPnT^<?UFvJOwMT5^;Kg}YwlBhl@-g`IBMxdpgX^hhO$omlm z1~z&a@5L(-YtMrpiI}o8k+Mk9k4*h;!oY+Q48;EQX?1hVpm<c-7Ka0QPnCL^+!T>k zH^L>kwzoNo>_efEp@;~14TgPUOE$SvXe6vq>N`+BrvIW36xo$`-x+DmOgG5cder~k zC%y&*`CbZ2h_BHtP;RetR44I!80OGm;H;%p)?&!MPGHMD)2YLaZPP{!(2d-s_LXk_ z!D;xJ*H7G6sFq^6fl#*PT;m6$?yVk@Ul|khSp8Auq(}Rbw`t<*=_As27YP5`i<sfy zQ3xNsZ94`r`|BpHnU1M+E7g(i=%{08&b-iSkl?5Aed=1o-LF!eF2Y~J31VgA?&*l# zm~p`HOB&%L8l4fm%_O}h@f1p%&q2=9$MefoA+?nE>wssH3s^hYRor#o)d{6mH3TRm zg3*|}A4Y|%IE=!>)u`jt1OtUegxXXSreh?oX|m}G35+eyzIkeSg=3CkJ|>@tdY)5i zd}H<rmJyF;@xxBtzLYPyc_sR`dj`I@lMnmScxML2h)SKq4<~jgn}~`|$_F$LvOzB; z`5sa3QNzoBxyG7jZK#4MV#bP9Yplw?h?G+a;bqHe!qXUo=n-uJp6IIF7hRrNZ<9pV z^D8T(9azIJ{_dvtUura+oyAm{#!@{J&8_|A$&-X=SSxg^^51zZ*M)^*{({2n*=|hk z2_}KqU3!8jECp>B2(<C~F-Mfk#=_C%614?Z8dYO!dRm=GL`Z?WXUC%P?H6?WJ72Y# zp?ZyYpDFE|UigMz!!vbW)Q<y`>0Pk-{!+nBf0F9g?}ZU_2>}8<;rqxPCaa3fpY%*z zVs`x7-u@{?j{Be?%vTB@h^mIHk5I|k$wi6ds^`qo13&ATQDXAdO@rYC$UE(miI17a z{;TK49_L!)p5(D)=AVyQd>K1+_bM)JD9XgED+bEzVqCYM@6opqC+<<a3soB5g@X0> zxzov`gyPlbsw0Dx8^T;(Msf?6p^A>An_2jZx(NWS1-_X7nSflO^rxfYhQ~w;MkEc| z;as+|Qn<;qMcvOP>CDPwO%W9sJX@iXG^YMl<(W(-USARgS74qXeB2s}np&5|^Jq7^ zY_V=@JYR_IMosN>ZzPiL>di!qH1%dW5XRJ0Ig8ONT|z$VJn@MY0Sfl1Bhd7xVzY`W zs@IZ(FMJ)*Pp*dO=}OxeiRxE9Pj>;&>1yoW+u?vK!yMn(^QW8ah%OXSLI3B`w380e zZ#KW-D#F>lzO%QLv^o={Q}oAIR+_X$UJxr<#FEJKf<m<r;1KS6)|Ha8;=*a!=Al*s znkD83c~zv9#Q!*v(HFB3z$#Hq@PyBU{J#4Y7IS&Ccl*B+B-c00N&9Z{=WyQx!1F>@ zG$}^SskeK~fh(lyz3(PTYDazXx(O-rspM|3fbcjJE_RG}L{ZDZ#hxtI6!3&Lk_!TZ zpPrtMn&Z}n;ovN#!;xYg_Yv{CQ-|Yfr=CaY54U7}<xmU_ZNb`^wqCaBeF78bBE@Tp z>9uI+=v?}S?@Ghs*AmLiIO6&?>qs%26@%5oW<#gKi_CU)kAp<o;&u9@`dnY9in1?A zXfe`Q-)~^QdG@p=LB1aT!4$pA;LRf`1o$1b^G~dD3v(u7aNNkQpbQJlIJ5iBLJ!tB zOqwtn;Tm#C?kJ2p-0nD_U|+9jepI*}iODSM9&baQar83B=Mms49}3utfBR%iMzf1z z%K^T_4GqpF<+J--Y@*IEW?JwzOph#uPj8&b-VqJM;i$vOi#^^>X2#|%%EKQ2j6|oo zBaov6t&^8Na#^hk66WOHXw9KFB4CqZf<pR8r=`T1-wQ2)p*^ky+rOvIB|k<!Cw7Za zS}d-{J|acLXbC&G-s+4A-74@@n)wBTP&f?I4f0c(M0gbV%Re9YdV_gM5Z^2td}S-B zf@&7LC75(mfXO(d_nTkhZ7Dumev#w6A{sxs$R67>zEXp!&p?e4Iq#TTg90Od;=MHx zKBi7*4mBJGk{0aTTJ*bINl!o9;tviO_Am6eB;`J!FjCJMn6v6t=lwuBjM@D|W2iaJ z^Upi$5VB&#y3Rw5r@w)|%~R!VCOBHmlM2g_W92>)vM9q}@%NQ8EJXA8@y=~c%@^0f zI!?rRlm2k1>65J!T-En>cEJ7$1GkI{Q6=rvU2U03o_!<u3}e=Kd^QzTX2cJ8r#%aV zO*=nmmx-AW9v7<}hvOR*T=Fk)WWE3#A9doftudMDm7a37<q;yP^pdMzKP<Ux#m6Ye zxIc}@d*89T|6LDyMzmY9BL1{j8+!-lyoWo+D^)YVV=+BK`G7x+ODA7CQQM0`<@s#J z8A4<O*48CxvT{C;tUrP|`vu47=T^3m!6^Q+xw?v<pyQ@Gjri!bW3uz(;$@|2f8&#t zHv61;>;1F{VDxc$*ypyq90N~%NZv5@+b1imCdlm~>%1{W)3n^}A9_OO;V9k~mheK> zIMF*VVUs}VCq8_8xl(^Ky*Nm;4b|YoA$GM{e9z-3Ge2wirzaHi2zLiB=i!E%qVM4w zx~1rOWJaFTA&n=ua_b+ow_ZIz{%DmU>6tW&BWC2+a#8DX>^8>^+cf>KEHUex;44Km z2VPh;LeTQOAQk()i{Pz}SExAE^T4@o8m`+Sk7$Pr1%q3LX4q!D=kU$*&+d9hO_-mO z(Q7wamLC{5Z5Ez6mEI9<B(NOg%-+d<-&LZ$b?P&Gq_6xFx=~j~A1&JOeyAdw{IwK( z1UJQZkEbq3`_3H#9S}chS^-L%*B99C5j5bkP}-p%rx{$@if~n}P@lSATPfJc8%*;1 z*WV0*dCy0JhSK#@L{DGYJ#sX;=GWtpuivdVX_9JqvOhwfM15GQA|^hpuE<3GU?AXC z01-dcrJ>XQiuwAIui?XfC6ib^cX^U_W~dp*Yii7#HtWvr@;!WB;?oLR-HBdfJWga; zAgy6+WOXWZAZ_S16pcqE+1`8{C3dBcADApQXjHo}p$Aga^V@y~u^WO>LFcGlEUEqp zmCrOO-n{(tQplr@e0#sr)=<`p|1$cZuS@cBVpuU8Mn&A)mYn{vOl4?)`S6>sGqa<C z@e$PT?l@@T=3c^z^zTR!N{E!|_O`huh?dJS>Y}i@H9-V46=ih+p`lYV{rzw}yu3u$ zO@HL5f5_*he>{!=LaPM@>IJyhf!4X!mFlT@Tr3w6A_(SUw(HFy<+>RzGRJ>CG#HAb zYa6K-MwT23)k-<V&eE9&var|<g#|K}AceuSGJEJ3Q#r&H!_6oj=6-k21TVigtCZ$P z&QN>x$_SEb`KhiFoS&8{%B7e2I8(oe=m;F8H;tkM!Mxl6VI9b<N6#hlN_EA*eZc+n z0hi5vsWzYpP-{W`<kjfP)qES+P5_sLUw|>E3B6i9l8+r?F37NNpTDFaE%Q~&3b(L= zJ_iXE71B4b@MVysj;@f24nPGHg`_L}Q}PE6E-mKcpalkQ0;P3=`}kc%F|2-GdJTq< zq`%0L(0e1)V5Uu@JLc?`_EvKaBhZ#jE;pge$Jky}*L+`Ao21T~Jaw$t?(^40o|tF8 zBspC^JVED%n4<=bYVO=;Tz<?ba9EO4`bVRG3~yZIbUw&?(f?#_ZnEamVv^QWl8y@3 zQbEgq*-M3#t7;@tA9t*$QiMY4ly5#h(o7~LeJRu`O|BS2o0d3nzy_LjkKyq@7|)|N z{;@56U6-}>XDOnVGR%JULzF`aO^!WY1WN6L@G>mtM|66tzk3H`3ImnWzsD7!{x>#8 z5~5M9<PeJpgw*I5Ys?|<195586l{c(rtD0Et;r(JQV$9C;)I#0P;|NQ%IT=59-HSX z^YR{Ewf^HK<3v~i65VKe8^>}z9gNvSMp_`|{8Kz(t~f*hSxLrcdvRy<+VB(^%Se_) zJnzRA$KHN=0)e)(Ysc%N9+Lm&%urm449-bXWJhsA(lv1lyR>)&t#?YKdIeWX&6Ng3 zSx_x>v7oISI`dokl}Rq7wh>t-QkkzTI6nPvMvHXUQEGSf=gK<YOg0!-f1DATS@w+K zqXP4QSa(szr&E?5q07WL;S}oVvu^X=0lyr_lwL{63`&F41y0lk6wDd0OODf6)xTkE z`##y94v8r?#MVO<l)j?-YnT-j5OK}hwOtxzq$Qn08-P<0Js@-V7Rh=iwt^@c&Y?U= zaxUgdTyedw_=6Du;GwcgvDuPgcV{vYy2<^n{H}Wa?LfZ{-F7Md{&1PF_M%t^pb=$o zSwLocJQWc0_`?K|?2RKJX^GU-Jw*w33pUG@axg1Yv&5+IGNP|IUrU}y6mvcj)(J0P z_HX-qWVdvqmEdV4&>-3atkvcEi~yFI(O^1tjk#zr#4a*ZCk6jMmd-M)t+v_1xKrFM zxND0R*I>onrMN?(xVyW%yE_C56qgd*ixrpR&Pm_z{N)E%cJ{M(W@g=MjYq*31qJZ0 zOODuVYM$Trw)Ta?!&NNH8VpqkD~3okC?Ph_%%45qM<8WV(}DNOn#*q3ZOeY<L5AeY z=HEmDb2R7W81lbOB74KJC&ew*jj^+U^4jyVjVRqv`JVA_UB|1O%#Tb%IWsnG%V1iv z$}gEVB#riIj%M~GrYAooV}lgFyO2MNQ(JNd^`>|To9vzHc-6P(^X`)qlNYL4nDT9U zBn=N+`f)<OoCzouubajZPYDM=QV7|;5P9Dt;+7ne8o9Ih1L&VjTH9_84k)nqeDajo zarkrn<HyIYuZaV}4K$u8IG=NFR(U41Zq1xO{m;rZMcJgVD$_>g%>)#(!5UG@i4syO z*_)tCC=zbj4O-Ta;!%)OkS#wUnQ9*>S)6S=6wG3xQ#A5^MzmT6_T@eNRl8X|@jjf) zq&(Jd$|yC7o|f0r{k`;#Bl5-wF)S2rZVKzRwItg>C(sDqh>#aT$`G;p*Q3|SRiL_< zOPnkQ7*W9|!+bU#(ukN<4mEw1*Rcs~oNY3OP7nWngLTv{crB<@!8anL-R{nMCgK!u zF}O%zxFg^M^1eT5Ip1mhL?SBkH^xa)<W!i^0-+<nxY&%PmTDsLdgS7P@QmjIAtE&w z|9E6d%U+H~4{BIKegcLZk<gMb`1ziqV8MX7LjfY#Fx!l04nu04jJEUA(p>q9snxC9 zf92{0>G$**g0|M#I4zIa%Tc7>7T<EucNr@P@ioEbu{KfvYtvUqmJHIz*n_C)ABp*_ z=G)2C)-y=$pH>#_=jYcIM7+1Z9`k%P%h%13_YGHg>@aNh!sF)UEw$Hc3Yoj$rg?yj zhgFUIMTB10QCm)0g&+#o=d<O`Y=Ox}`ww@$Kigq;i<2yU7Gs2#GV*5j4|R^L!b1!F zKRQZ9WjRSu%IHz?@Dnw$*XRx`XDFd06MK60sA>;zju6%;OqPp96Zk>ER6`yL6Q6(? zk-lJQ>1nXT^$(cDKwLB?S>xxpT(^4y;hALgx4YM(Z2Cl2C6N(p3<`eJdA2j32}=0> zkQT26rjIJ<T%N)9Q+5h`FNq(I$u*ByqQ+&OB?q9q$y#vE!b2nmX7MW`M9mYF)MbFS z)!~57j_2t7Xr%bc6NnO@nZO;xnF75NPQ6nOlfv3BWG*Y`!Mg<CU2+#E)pH4Vmhc?! zx3|Ke0ZWJ&9#6i{vujvPK}g*}pYZe>)b&iaNPOB$E>(;4+pwEQ&t2DB-z<mz!017+ z^D=Y_SF}VlMA&&mCNskAzUSu>ug|4!T;C=sR-5-dxiF%NuK$sM?p$k>>}1&O$>Hz9 zVg>zGT|IOa>)lVh`W}j^=XFbW#7S;x=bsMZMILb}5<MV<*x`|7d)BUi!$`_A$0xgE z0jYK(7x<lpjccBzNH35haL>|jABWU^jg=SNcJ}$<`zVlcHhy~g=glC!e-`GCFA}45 z@4vard_Q-J98x^_`}&Aj;9Rk0F>$FjXv6L$F=e|w?%;-2WYPOH-eY&P8TmD-8RQTR zM6>iE4M_F^_bM=U1MKqcWVLu|eJQw($2`kEV(j7()=EElp7!o4fSl~CBTEXClSA)e zlNlh@Esa8h-5gAlaUIU?*Xtknst7P}Fr0c!1epw+$U7K&$Z*J2)I3S#gfTli7*wYo z9LQWI9QH7>lQ%O%ThH0|G$lwj)WCi?6&);DQy7~a1jH|vbf=Y~bPpjznR@X}&J?qT zdat2=ltNFD*SezKEPoRYVmJfac0i-rm0ATDKl2=GG39|xV!B>vVk_uH<`I~qIHQC+ z#s|n~v67PLhbJeN;9!hh2)XGbAPWokCp+Q=z|BAaW+MR@0C@G|#$YRPr^}<hsLc#$ zi^CVSZiD4B^aDd8_fT$s>ql(XQgqG&`|wGumo({O4a(2~aZpcguiutb*9Lfa8Z+e8 zUxFu9slN<wSx{77JwmIcg!5~N;R|OBEck1h>NXfNIZ#<($ZVdJIxka&rmb=so+DZ3 zCRDqTAaMSgsiJ~!d(H56sJ1zXM!~+nY<Ya(Az~y@k3ZxYU8X%@`?G5*k+WxblXu8V zak~^-RhzHj3NTb2G~U^O=unYe@Kppu!Iz@Buw00(<mw^HQ<Dgic;UMcLa-=X*hi=x z)#k^ylf@=ZZs6SEA%loZ3*HdwSs)7Xwz3S{iT=1b)?X`j8V?{WS0I<nOx)}uK|5{L z)Yl1tSg#|puA||T&KWfg<kd3*4f<m#p>8Ap+zX-ANcU}nfE)O;{b!qVUskAl6zw)Q z#&(K(?1S$g!0ozP>Ef%AoNO~opg1JYgo@3_5rn*CPXuM=s858xd9}`lC;$pa67F5^ z5v5;<b)cw7xf#7@>ubF145!$5C5|6~lBWfa&Ty*m*7#6G{}9ikyZH4Xb3{oL^P1-7 zWC#X1En@y5^KJ8G8zWP2P+^G2e!YM93i2W&N;Sb*!jKhMLIqpKy_Oj!bjqnG6=9!! z^emJM2|=}y!DLJjOKBZ!(Wu+{C_;Qs!Dsc<vvT7cGsFH#wfAo$@>@r7Os}I`GfLY; zNOXuDPa}k%crmCPQkQ@*;O_c$ee4_WjV^Ey60b^J%XeB^EeK!Q-PoRWUbi$pJUY4j zOE$q<hg3<xd>u2T?gNE6OWR693wj{oI$|XKi@GX4E)c5ub{?Zkldb&dcS$Cp80bqb zae*2v?8`Q+wGHo3o!P87WR8hS4#9Lx$NoG4lWdR{q$Rkh1|R2GX7OZc=cfUG0fWjo z@6&w`tWO*MujpO(^7jgQ#JcDWDBuK+iocMAoNa{2J^5e8bnNMFsl_(xBpf1D=Mmhg z#HId0+uJHUpgNv{4?-?A^~MFx-eW83`s+ZxuOXTOD_Ifx*^MQqd<<v@xqKeZ&z=fx zm(`a=s7sgi%h$2Ez_D~w5Hwu$!eS#jV9ho20(yRh<!7_Hobu=9SQ33DeQ(9RuA(>s z$$mDuF|FHK%xCQ@`?_39Sc@6GsyLRoDn3)zJLM)7N_4`pInRSuB1Yi1`NZJz@R+x^ z5bN|}*mQR~T%OAc++7;<P?N~$s4yDuVZ2bgVIc~C?f^T+ZPiJS%c8`&$|=CweWva5 zZ?T<LXNi)mkmJAS??=};(wd?U{<KMPic1f=obe`q@?}TT)6tg@YP5+{{T@~D@XR$s zWa9?gYP`5i=OkmpA|+^7AV^}v)d~!aVR^VT+9S!HnK3IgfrhDsoe;&}GJx~&aaTda zd~)457hAMOVq0x`3G%K+;R>`NMn)Zq>OZw&A-p>1=IqtVsQXC$Oq<yCJdCPaNnK9B zj{Tu<WbS2X4=z{uV+y6MHl9`o*YpRaU7C%|?Zk>0jwIogTiM*o5u|j|(yAfz>p}pX znaLuLLY(JrJ>})27>-^8fU?NuSH*udF2KkZFKOmLYx6C>?nxD^u*$FBC+y&_4i$V9 zc~)(=8P2PuVme%Kf?`PIwzsak9N2E~z9J@qKT@5U(hmQqDGaleSXzD@Z=_VXF9~xZ zO@17Okyb~uox_*}rn<gxpF+`J>K|Td8mA)pnt`Gi<vm7F>`QCpr~VUZ%deo$KwVC- zsJO!eIGQHqBB{6pU`8hij=53L#4h_vqhppt=TdX?erS}jhp~HHvMTO)kNYfNSVD%) zevs(J6(eh;fV2zaOrC_cm;xt#=1|asM<+M_uaAI!K#H?Px*#6uM!1IG#>KB7@-=7r zoL?^EAp)cr&mD<+Byy#7RtxkG{Czsp$i0ial@_*AHHu08mxP3&yV}dmHKRwnO0sCa z)7HJ;2?>WyuBNT1;n()JBuz}BK{d^;ud>VHiCLl6Iy*l__AUPc;xw|7BcHfwy@2sP zM*t=ehL-ltlI)pj3j*0G6GT|)Elh|o=a#Gd6I#>Bsz{O%*<9I3sg`Ddu$UX#ScTPQ z%vCSt>S{xRZ=FFmwzwIx4L3shIuv4GlAndQO*rN0kgX;b(!y9Z$TrAhlH`IiW~d1; zn<-rpfcSfbnw7?@nozJuvBPi5;HG0olB3X|gJXI#UJE42tdlv`d}YlfFy%9I5V0)2 z<I$G4rJ#+3^?PbECJr_>a*oc^KSneq2{w79e;5ke(?`%WGjn$y=ro@{ytb(ScS?3J zsZy|W69(2j$MPsP_^IWV=B+>@naCW^_IaOZCURZD7aj?e;_e;j3H5Fc@&_%Ek&<wx z$Bkpk3Vu|_2liS_%CeC}Dw@A`##!`#$f;Z0`IR<I6vL3TXSPh#Wx=tmF(S*`H`=wK zl(F5Z%nl-(R%&H<R=9V{dQ#HvO=uh?UXWW9!LE^u5Ci|T%)Z!U<Rk<VnUAWanGX*! zqv0%45WOQ3-5D<XT}64G6@P}BhIjeTk18SxQpNjy^J)(t^4QQQ>C3oHjnbrMk#NB@ zhS#R>W$|KtuS&~)%W6(a11Te3@?kz6t21-5;sqOd(Oj-+lnY6LD4CTAM^#3XKahDe zibNb9yD<dV_`^?=mIx>^@QX@QtSC!ijkKMyj+!>_2-TdO2~(VxKXbF%RNxz}IpiEV zpToLRjm|>mCXc5bc9n6j5+YL+Wb|1D2IJ*WX3J3#>n^Ah%=nH=6jRor*z}pny|f5> zNX#g^zT58ED72=2jG1KlKrAy@r&$~BPdk2!S)E;plFFaR)I$RviJE<J=1At38LAmn zx$sd|A{YG7(HRHvpR3frB8Q|sq;c6u|4~IR+8GS36^XO&G=*6<9&$)V{u~qY?mXqA zol~2%5uUP98p73*;a1fZvZ`{t=NCbi`OQdGk4xUzw$b%whMru5t4w*GBG2>O((Qzm zcQ&zj<nZRV^=iYms-r-p(L5-^{nRJ5uJ@0PZmWwFHoZx?QocobEd$P1SU&&Lzw}(5 zxUG?gh@O(5tn91`-`N43*E;sBVG!tm%ISS3hhG9>1=;m%Gg);#$*Bu!QBiQS`S|CM zkj5MdEYdw_s+amF^%dWq!Z0ycL$0jEIJ>{ZpDI$0R<-e=m9mx{yC9SkIa*dVBZ*(< z(aQTrdpF8e#uF2&juv?$=hmC<o$rJj?=|XZ8(#D73A)ww=t^^s=lRO)Y|!f{gx(0q z4dXNEE~F0o^jdVFNT(S8dMSH+?w7plar)w!!?pKd<aVDS>x#94jeyy#wD~yEVm31K zcmY9R@~>+RiR{(jRNZTlz3(~?WL3I>2lA$41UX43@yDI;=e%vhs2{6-9~IibO&ZkM z)cZ8VqW<h>r!oqZGHt4+CpI&?ix8D1tAkiJAH+3jETl5pST%FfsLCc9lN$xVnkqcn zUlFaJzxT;Z;|zm)e%ZJZ*0YTzDI96>Wg1ZByh$Ay2vhNx)$w_ip(7l?Japuc5|(_F z82t1}aA4X)DN|NbHp3KTG8lGyBO%dYJ~L7*6Hgqur~(R`w^^tHnzoKpsihM!uEddb z*Ko1zw@^He#`>TKg}Ei9jd64EeGkKJMb?5X+8Q{fQVGIR^meEvhQ{eYIGp87)m*tV zO->djb4qCJjFMn=N%Kl?r!^LnkQ*Q-0rPoW&xVD~$7_pckjfJMGOp${bD_PeRc}ho zizY!os;_OPkH%e<Okr@V8yi=W%=6yeJjgUXZw?Kdvz(N4jrq{y&GC35;+Yhgfu4qt z5&3g7VL1$lY$r-a9f140p28<%P_DEVjv=}fpH75MzC~?b2x^t(D@GPb*o+1P)b+)c zNao}IBK9x$8+iYKHKm_7&@L?%uYz<MHRPYRI56pfH5}w2-=QN$xQB0BmmI#7nDLSG zyTrt0^`(Y2Y<mW#yx#izXN(ZO<#Ep_j;`x(Pqp8B*E@$~-HeF%X2o}qb0%Fb-GdTK ze%?-eHr&4mid|C3I-^@>yY9^I9sSyQs{zA^v)_l`4trbd3nrfrzPq{4-|KQp;eZ}U z7>RAJ$BK6azQbo;c*pwp9l&D0TYbDF{eK0u3XbA2YM@dSo*r&!Lu=Neh$qmDq;}ai z1d|~oHa5N-TpQ98x!OR<)0WJajf4_e9FiebGHwW;`9y5iWYN6@Y{?S~6i{7d7yhEZ z#o+E&IYDqxcRaZ{*zcWV?UvF&LYP1Lej8>9yRf^->n8>=JyGZS`ysxYH0iY2F|um^ z%E8=Qx<CE8Fs?K?f+x9uyR`LNF`AOoN~fW}!rUukxuvD$k|t#LezN3x8<G%{JZ($& zUjI5??r93+EJ`%`e%h=zCp)HKb#|6&Z?O(;3RsK2f@qvr2ma1$?s=c67iLgEpeqQH zSzjN&SZ5$F@;HVMLP$V3rV*R~ZTfvQ5gx;_a`ex)ULrcj+1cX1WMeg09rMk#E<pe6 ze!e4ob9+m2WqZDK;@t@%Xz!i;=8^sUoT%qeG&|eKw;_8jQET@WV;SYX)47{z+S2y` zp7^uU^K%Aq=}?IkcK3jb8h%j7pw)(U%jl)>6<``c03i2IA~%D7l^`4@pFX>C;JARE zCdl~fx{sIr;8O-|+B8!k6`#-dL!?AhflV6}bCzz%31_i)ie%LHbzsZOR>Z(->?f(; z%LDSJ@(cLe>}@o=eYu`PSD+!s1^O@*hY1L;{@g1zD=RxACpP;8=OXv_39URn{kL_$ zlPLweE(K5f+FEr+(Kuz{)kxu-PfLSk8x<5HEt-()%Co-knse`2s<p3OCm^lg^r01U z9L0Tt7kpZduUKiRpBgF@s?T&>T#a7j_#yEwimO7t<BXwPj@}U(F!i@&7Mn$XSvsl{ z@z0@2ac-a{%Qot*9W25!8{?iXK$4zH96eH<L86Hz!dYKk#wHo>XX)lK3HE|XMk7J< zLNL-uNSB)|qJhsg%Bqtrfq~aw;x2Q0g4*ti2~w@>lZ6&P1A_2vQkjtYZYypBAC;82 z8TOP0jTZMQCRei8ZBl=NZ<!c}@DmCK2iNjUXYs3K_`pFsjT-&#lIc|U{mzC8ya=n_ z!2~vnQ;;$EJ>LH%e`?4XS*a`mSMY9-{_x^;W9E|GN1-1n+#Z#Vba_@EpM-OFkWmsE zhsf_W*e>v}fHWc6O1xU1391r)b=ZbHJf`5y=%yzXc&2RWp~Nds$59z|gXSr<6Y5@| zXhlM&niwTU2LGJ50(JbhM}HoHY^0u_pt{c6vg_u9L9L-_JitQ|^!%zHY0eGh!U{Ys zo$L+)agNoh8T$-=2dQV-6fa=r@rp@LVoClPpV+eW<10<784NFOT(I1^M6AS)OD7y? zGXHBzN0)#UsEm#g&%yZv%LIrRP*P||+Svc$HdY&4{KiK%o{gg1j@gc-#!I8dN2NcG zVC^+^bMnuKaF*a6s7#3_Y9ug=kxVfnQHF7dalmViCW&q@N%~|g-+%g3%Wkm<rs0^% zDHNb4vp{3aB83tZwBYks!y<v3&#!stUGq0#v5YFco=a-l5@t1-%*a=G3AsJ&RvH4D z<DUq05)<icg_;rrl2=^WR*-No_^BxtDH|J`PZUWi;ZAZ!jEUzz%PeKJ3qx2yuwsRB zE<C4$Wz^6T^3v0Bs%mD*kKa1N-QRq8bGfk_@d#MTuctr9<7Z)Lb_Gxs7(FeYDJU3M zV1NT;kLZ=`IaLes>DU0sS`Mp{z0%;8n&#v=F)A2Qfu`$)e3G$#Y#d>H+2l!Agzdvm z)|5p3x9T(k05%R6>9_osJ?lEyVpMPg<ZZ;MS|3d{mLxlp#G*z)Lt+J6H)|MDI3@CS zgrCM`A|492dz1O`k5@|M2O5HAN@$5kmv<=OhVquiA48BU#8hV_lH5Q+{0%<>P!c7= zd1hyMyS$)|+V49QAl~h4Br_tNiMiW(<@;&-(2Uh=q`IEV8TTj!C^PGqco~U{cI+mY z{*_STLK3V}Qg1;aMs?ZsA&UY9K$74lM%=Q$a-efHXI`QXAYq=wP#S`W&()cNVy2-K z3c0z_nTMiDZp$$rfd!X>yoPOgyDA6+0DhTOh~L=+nUF>%AuDkC^pk2+oOYD&Z{rk- zZve6q^L?l@F`a}z(7gKoWUuQf(HtrCRT4MQaB_8_c7>bwnLP5j5y2s-_F-<A_m=%B zu2awTd%}SCg+KZc`99a$Pk5$37Ws0^u^XzO?)|N-LN=6fS&p+|{v~>9?9ZJa3q1=` z;k705kC^Y&CUGs@Rbj1xY({%<BWa|N6tbu~k9EHVX0B&dW)YH+m79%tZ!<49J~E{| zLA&&QhZe=$!)yfH=gq)DW)1AbA};PVR)TiDqAB_<#5<oTL<H>S%tqr(-?sbS9*sB( z@v_+~7Iau0{PrZ-=-r99U&wOst8A77(eud7xnKI;hnJ2h)~{)w-p}887>_9^aq5w? zI4JHpuc^PSG|_fi|Kw2&ph!&3Qe}AQ|HwDH=IAH*sX4j^=HMK!=veyUCowyu@%Q57 zikKlDTigE~1}EmeZ^qPt!iC*3G~p|qLzv6L-KS8c2nv8l&5F_P8Z0(-(@3i1?8~MT zj@{Xn_i&bAHJSB9jU6wd?F`$)?qfMPVHc(~x+d_yxbRDh-;lm)a>0wx`i#TQ5&#va zjl-(1Eb4`7O{A$LEj;8vC-q9L1I@lf3--Up=#yGA^5lLTY{gJ|dno^u%=i7!No+Z< z*D>Q#qSF%r0pY0d;vIc~#KMMBa)Eajf7zQA8GF{YN4Md}DgoDYDkWK4br_H=Jt;<^ z6W5-MII;PAt62^~c5>d9;pY#B__<5Ns+1Lfpt6l4xBK+FZFH9SMC42^igmL2>T6@) zFKZ6OZ>yE(sQ_a^uUkxQoh0T*i^liHdx8rtO3(lWJ2>YzRBFy@_|F_aZz$QaBt_JY zC(!ZE{+SJ2SlzAI@?OT2j^d1-v|U&w4_)&gHR#kZgkD6frn1<+%*}3ofej905qjbM zM;=-8y+hKiC^CVWH(Q_!CBE7{M2C(y2~v!K`_7p+;<J_J^ogM$@|4!HYGy|7x&4b> zcq(^28?2A58MSm4yjn>bhtw=oIGFFH=ai~?97|*&z;F)Fa(Ba&KgcL#UDCHd4x55) z&;cgMq-H*Py>cl}B(QY!sJwPlNeip)iiLafocV$9wAx3gt<-N$aU#o2l;`>Hckfvx zc(RP(!0#r7VvG{LQofWeZ%jXZw?KVXf}^wj{ZPr2EDleQUmE%wlKYn~N?G>V{z~XC z*9TjV@)9}1Hc0a%3VC@xTuBAnNMN`k_$zq8J0$t9TA`=t<bAT_<X^6r!gRtr<UV51 z8CSp_soebt4)0(ESgTsHnsr@UC<6PV)&fh{kV=QIP3#U*TCJ1NyMH>cd^<>31wt?1 zjX3d#m88CK1l4o*zcKp$g1$)mL}Z~+8}th~zHYxA#}ohKvgfx6s()N8t9dK$K9sw0 z{M+xpdtQ(Tg*k(KP_mZY6BIITiNOQO!x)qnqq!WE%fARhNgHFQfNNaXQPT0<EA-A_ z0hhV`^yc|fNO&Q-;}eMkQbvNTjM%Xa)<F&mSG2e|9T>9_87B|0C)=|h4eiB5gx!v# zBchk+5e?Y<E0mhUYE{>(7lfAxKKR9j0E{$qvtMgoN$NWkpjqPBGC<aeR)uib5SSBq z(Ch+T^zFiNbMT-67UG@LLm5p6b_Z-d$n;H@Ac4#O%d9Ka$EFv3Sb9I>)vmgQM8Iv) zTL0#$DDDlm?-d;vT`v)QK)^bJJSpCtUx2+Of)0ogMoZy*LOUY&nMP*VVK<|Cwh<MB zlSoWeAQW(DLcshca}YBgwcLI9q{AGJ#dze^@Yn2)k9SO?gj9?Y96qiKRGVH02~vV) zszebaC~Ir}EzX@K6LB~r(lPJy3%wu3`pdfiH)dnp>qf&Nos4wCX|}U|V;X&ayLk)o zfd&|Rx*K~2L}Ah(`Tt8U1Y((#X^Ac7bd+Z>fj<xoVCl-gns-xDTD9pRBfFqqtBoaX z*-O;^NOM=DtI~>kN#|a!697LaR8i$Njp|Nw$bjozd@ueD!eAoME;-vdk+T^bNV*-3 zFka95zG(O)_-M%WQ&r-XD7}Z^Hiu{Bqi-xEI%(QC3Y=kABF(0WLWCMe-9q3Zi#L<J zsyZX0=WZnIC$5BBorTrLfX({}0!2mx&XBvEnV4-~yWOAQKDQs@i)5i8<*)8?^rz$i z?2(lg*x=XPe;oO^#EwgZ201#5Cu5k%)9V)X&r=gR%Z4m^<GOqIY?@^n1x;@0J*L#C zNVu9G3NPD|R+X$~>1eVmJYtOK2~`)byX8I3Mi0&LbbB`Ms4}i|ug4S{3R?c49-(FJ zRXh(F9=iC+9m&;HAsHi>2?LqqIx0m1xRgWyj7`YDc~UF9juXC7Vv$#dtbxkLaj@Ny z)?zIDJ4HCJxuy)h6Y4};qbhO+utl_h4a8}?ZYAHw_I4>pXWj;9_r6$sa~v$Bjy&`r z+@KyUK45p13UMps<6+}l%-7WEg58x&*24aw07YF9CB`ch6H&*;o4|f-M7KiU+*Cj4 z9SWAK=Tutr9T3%Rfs%e%1(h1)4I-BVyH}lpJ5g>^lM<lP<EY9~PNl<mEO)MtO;l-0 ztwU%xq-vd6-9(GMWApFdM;${w!wgiG*|8=ch(mcJ4=^%=cV8fWxG!yafzQw>fjy9? zRwj}a6HG-G$3SrqwHo?5sM9yS5XO3$x!GJ{t(4w|Gds^{o8X-PJBf8HUBE83i%(cM zuOlbGoW*u;hI=w&cgAccuQ1ohH{ShqZP)EEa_$oj63!J~rMCU8$;}A^m5xeZlb+Vl zn+@4cssXJ{-|~W{L=nbvfS1+x5b5bWT8O(9MsGAgnM~LxrLC_ixlQoZr>PeWAd|o_ zBtyeA`F#}eQM~J)GWwTKCKE}<6YUyAsi4x0nJ!_d7-#B$r5qNmfcS0OrbnCpCw=&d zB853KmA>gc-Zu>zBc1qX6%YiD&SFE@A}xjW{|Jn=S1Alfhn85r!Q$ljlR;UE-OXX} zNJvM1X*NbsYORJ>tYMl_MMZs{WpJsCP8DY9bSf!AcJW#EwH%?c-9I3X{31KivRHW@ z$D!}cg8GM7T-(KRCWHkP5LT+zlELQm4kanksVhOt<_t~|V@gY^Kj`o&fe%!h=dnZ1 zSUu7t2yCvuaZ^tB?lrgT%Yi<k#?<C#Z14@2S`xoxx`v6@h`CeYSjI`Ly}f5CjD5GY zq^nWiEJA9QM>Ur(8XJ0>XvJv&k9!_wx&Qo(!lyAN)biReg(un*78Z8c-;J2u2YU09 z7)Iba;z$%N4MIL>Xk~0PeM)#{#A&W*y8<&>AXaPDaoxY7ZQNFV_M~>8)2gOUhXE+_ z%!&)r=mJr#2?H$Y^nTSCwnpuWfi5=#5(;}FMr+4`Qx@3aA1De9>d#n_wjkj#Z-d=J zb~GCh^?m99>4fJXHKluv)OmBsKdD|NE(aF$#2mQ6lfo^?g-xc|T`JuDp+rV^Xzk2x zBH>eMf{iy-pIU6J;#^t}Lm2`RjD<^dPnPS@@xU!2LJ8bt>LG4f>+YpT7^08+*V4li zr;Z9+_*B%Ike0J<$tP|%s~CKvl>vp$_f{<3<pl+UzMhD|tE;q{>gU`CmeCiKru!cX zLL24Qd*oxoyGhe*6Zp2zJQDSArKFAcu=8;M;|$Td=YDS44t=8rJJ}r>v^6`)<f&=} zOb#OZ^$uZY0VE^74hw9PE*db5<`F6XfG3{c@68=Qk;^Uek~POZ+D?D2z+KY&`^#DC zQxlQ{q`{B<uaH}ML9ju}eg||#PKY)~8<MHGf=Yw2*Uz_A(>M{`l1$nUUXIfs0T8UU zT@>7=*A)w28?vA2D?q-(xm${!v^+lt<_%AXm>$y7+M4+wEL>;gkIZV&lp#w99z@&z zRY*-5Zkv#PQr|tC(w$t_b#)ceBTG2`@<uIEQdg%jZ#@pZx~gkEbzI56ENq1||1T=8 z?>%(p>ULL2SgP0e^#-{`s1C!V!XGl2ID*i<d$u>8!H>^{{4yJSGM`J(hKfbj4CBTb z;MZ=k{Ro4RI#4SZPF4~bc2VaD{`j3VZ+ZK~OFZsh;C~Ig#+RoS4-!aSkNo!G&vkQg zBK6;}FzGz+a%M;jgGNH=v(V~ZL4sB1(vHT~bTXy0c|8pa1y5iW;fJHx_w{uBd>__U z0~pGaw`;$yyL(NVsxi4yt@XX{m=Tn|89G?j^=@`ZP0Zx{bP5Z#b+6g-APez!A-tFo z5;s#@R1#KJEN8{nc9pAWgP4@UFVN1Sd`Kin_(-s$i^H)b8E^p7zX>c4>f9d#fm9we zIPM#*OUgnfyI)BT122R}B{fIcN2AwmUm%Y_{Pk;`UA#k~425@&EOd3}7qj?|fKsjn zfDu##K;7}^+s!FfSR*AIi2TQH8&ZA+q}oD*0MpJeq749a>>}{SH9k&K+oGmK)!i?9 ziq6Ey#gAd90os(TG|~_2ktr3tZ#_Xd-`u71eq)dcOv-d36rM!+Lj`0Yk7YAKOMpr> zSC))CYAfGa&x6tD+0WLU#sui%01m!kHSnZ;8H+8oa^r{QSFO~PUrUXZZm0qy6=YPq z50Hp*Wn2WkZZe48Ev<qnJ3^0CSrgE2C>2PRhg}hr2rJ<jw)Hy&X=uFyCnog`4|&#j ze<DiN`5t)6T$NB8`QG^wFq#*>KB}t_`1M1yI-YggCCiN7#P(isuE=6JMlIptaQX?k zx#dNmn%Hn@$g(TEigs{o7iZI}%a}$2rEVaemQ{ig*%f?5)putZL<kBlI}%4xem(UV zg-TVao4g-@NrO^_OCq?PEt0s%vBFhFkQsp$zBwT&NYcNjU7v&(sPG$LC~mHgXKoHf z?nFAT4KPvm2%HZni2QqRhqa+9+O4fRWM=$Tyl3vgz3N*BGw!}$(2E1aQ*KccDWHAY ziy6nEuN4jVk&#ofv>Y*nk!vJi2oalu&N$2`uwdhHg2HSEeuqYtIe`$EA$;@0mQMsM zmDHfgMgXTcF2y(c;VOvZmiX4Jp3;$lLevmAoj{;kZb%&)ELo)w<OgU2zTA}YQxF$c z*L4X}1($7(<bP*6PVW>p_Y0sTUs4#KyvM~B?M71(Nt5q~T0;%+DVKs(eaR;K^%(tm zIgt4dZ#vkv2)w?YvaBM{U!>FMUn3K}YNxba*H98zCOjv-DNru;%``~EDoD+bBq*Q< z^}Q@QOG^wtSm;n6S;w&J;~;h3r$koVfu%Y||E7RsjfY2=={v>Csu9!W7k}3BwCUlX zo_*sB_B;Bw&Ydl4<iI8)1%MZ0a8QRj-jSNbsyS=U6(Bnf1)I-ASRL~^Pslq>OSFyn zh=9fz;&&^)uKtt=(Ql#rj9b_R*qdgF1<5Mi9J@nz0g3BN8;TzO+t)t1H4dMuG%Di! z5B%R*H{<?nvTvxRiy(<+h?ZqsGGOCoU;BVKQK#Hrwmi08_`iEaM5fmC;}(X-|J<7* zC<+|6O*Uw+X9YPK5rIDa1VgCn)G&)*4~ELT(hsF2MnR^ql#08<8q+Z*gSWXe?^Y;p zJ!NSGB#|<@;CZF=+GhgfCQMc;8JokKS&wHL7GJD`ylf9P{(Sr_qfmfBf*<nZF$Yef zIA=bTM%^;6I2qbis73<7DB)^VbsZ6O52y}=2KE$~=CU$g=o>eyNH9Gzg|G%TYTZRo z&^||qSm9lUb9~+erMbE1>_q})j=_~bWG&OpmZ(h>6%}PkN=QRG?Z@U5=f1RmG3q<> zIt4vkt(~>bEk8Ej$64sZXr|{~+S8rCQ8~>a4}F+4d?my)c_uDy+CD$iMlMU?wa_m- z+~!wU_D&!~S+$Xo#iqM6a~71c@o7Y)zIq=oI70R-4?$47Pr6=<C3eAzjf<Uwp+?9b zgRWPeN7Yue*|gWB%`?QU4pzhKei0Ra>Fx7WYC?jkGOJ1IIhpJFwWl9ieamn_LX?uA zj~DH+5a;fbcgNVOff_BBz3wI7+C1NS(UbI?obJx^rw|fBEj&w|im50qtM6P|q7)hF zD@-QZ+HpmFFgt5G^L;nnw}Dkf<O@9f#+N7TXsO^7r<Lk20}TyZrV>Xd%@<Nt2*2Xe z;HOHIC$;()nJ=Es+5On~+hea93E>-jgBfwv#4|25pc=-~eb43VMT%Z4)_MVfauGp% z)aa&RTqLBs14i}-eLBGs6<k0xbhSuL<qRP?o5aom$T92uSMUF!*SFTrqp8k_&7_L% z+KO50btn41h~(VVk3UIgsz&*BS$hp3r@D)c&iH!YVc38XT-Oz>eG?s;ZU3xV{#Ql# zId^n5;t2I$y8Jc%U$`UUpKD>a`@}CN`a3d^5MsUb(<C-A#$M+X-i?Gc4G~QN;h|_D zv?BwvC-#>W8651eg}ANf5hXm<V$x+E+se0DuH>06OIf?#frxC{I}=d}iL9li3th-L z`mb<zT8I|y#O9zi{HfyMzz}tmX@GDJBmH1mQ%ixJs;3~$^i5)0t_@Dh-|saAj5m1V z*fTY9xCEE~bNB{2@vNK%S-y8q=6-^nESlvSR=$;ggTSK8;a~41uHuV^mUCAZd@tK^ z%12Tq%NW<CDwcN5;kzf_^LxUmg{VC@(u(ObZOX|M$h7{RJVKIL=Lp#77G2T6u9@f3 zR-5FOsQ#;KL=6l5zmNK6QVEzV0Kavr-)alnY+`6zp+o%(HqN259@L)6C~vFztc_g* zb88aEs#j#wI*@EDH*tO5UCSQ-kM>sjKB`5Gho8Wb?~NkmJpT#0xm2@Gr5Dwk!(6gN zl3%j+vL|gszQ{c$9-Zn#@%&7BZd-qcFmN!mUlv(dEFC2HkGXa_FeWj<N-A?VE>)R> zQy{NnU0|{ZaV(!P+n;SrC>Xtccz}cC01ppG%BH`zl)ReUsSenwrqZC>ClQ%PXw!8k zv4<8NC%lqvxDqS}0e2c@N>Gs{1o+bx7Xp1Zgrp|4CXF8Szbwma`J5(k<tkmNTr&al zaTNy6ij`pKoJuW^qQslPl1qcsmX(W67FIN?N$N=I;y~8omwp$<ZOlp8`oV?649h<n zXGW~MySxA7Ntu7Gi8nu&wb#DJb$fC<Z4ly%6}8(qDOP+hH6mj1Su|Wzra&9r01sru z7uo=ERvto}^@3D}bSzgj^~&bk0=lUjpA8daCX>Fac2s2d0_@=$odF34(L=)Qf3QSY zKR%H(Y0565e90qLa0@N^f*WziY5T0X%!|S*n@=L+@O0eE^e@Gn^1c!0b>8tQmbwnB zPI;2Pzgh^0!?S?^F;U);eSXY|)ac>w{oaSDvs)YefkJjXK*q;zC-y9I@98p;y2qNA z=P@m|tMQ^lJW=dN4-sx^5FUy^lWXJn4-^#w>4)=ghgfSJ7;C&7FQ9=_t_a8k9o+vL za{=<P1jrHxLacROm}6doRuCQwrCPY$9W*n=BsB44)fwQc@sU3}j4lRb0{>G(2fw{n z1}X@sl>a6WR1%CL3SL*ztKIJ&Y4zE%XT(&FH-k%JPN-XR><z8Qpb#1;L8U>-c697v zIT=Q94lY;9?)xbF-bio$6bI+F)qY{g6-M2!$}MpPi8zvK4g|d=k3CXKpq<$kJWsVh z&Y)9}6Uh#&cgP*re5%I?Hx;*S-#Uxdm+y9#?6ceZxv~WES&$^}beW;8Rnn-eV}Kk8 z2s32(-51QZ)oRRTxHE!XmpHp8FfK9HB)U?E6lbKxa<La%P>sCo*ZX)!nPuEggawO} zY6d{iPb22Kg2mTHxL+}1<l%3gfZ^gXoBOU)wu@-z<`#pze7<z|ONyXYcA&d^Yfskf z=T+(aJmITFKv$^$R5mh7G7?=r<f=1iVV7$NO=j!g^Fn-gv_#e>j8&sc5u(J6UHVRv zJ;1#0mwG*g*T4I%K@e*p)H$`fc0!h2j~#3{wINUNFn=B*Sp7seIs|EkLQ)EeaNw6= z47$qp!kV^d{;zz~XCdiq7cGC0(SP<$h-XYfNu)b<vEE1~`~z9x^fYbDHuxLzER^RF z7q|li@{5CY@*5Q_|IZotmp*^zf!l!Ddg;8^?G>@UlswtP>U#!{QEOydZ%c4|FC9ie z$G^0j{P%@>@;_Z^Y&zm1{=OK#cUr=-slN-XNmk>0^zzomP0};3l(qEqNbEsZE?+*{ z`Oy>`wx~VLt#oD#I&|+#Pv5^nsBQy;>WbP{ZXEi1grx|HPJ4)Ic%B)wjNDbyOZXen z4lL2Meys{8Kc*#oJb*AMj1sjH+-d!4sv1d6C8G-$<bL{{9m13;EDZcUqNFoQZd{$M z*76wD!o|0!uT4z~y%0OZms!y7<PuI~3*hT3rhc@ABt|tvB6|l)j^cn6xRxsj`}Qbl z15gl_GD4`nDU6mtxpM(w83UOqGZ_oMEU%4HtZmE_@i;8^)1VE(nV0sOVfpt<4NwiC z94JR3V@dlLvJLtU`&N|cPNc=`=oE_(!dg)S;A+V=95W>46RUOCW4vQ2X#sNm5T>1D zOc!FU#QBgDoD)zG_?`6AE;1bI&>yK}e9Y?v$h}f_^cN|I^@OG*At$!jaUE6dTMd{Q z;x?$RR4tg2M`t7@Cn`%c0X95k`XFy7eOmXsnZe|OlDYYMjc4uOty!<I<K;!w1Rr2W zX`lc2mvIl<<RfxVHQpel9*BSWAY$Pynd7+vsB#~tO+8YT3=ifcpn^_v)!Ycxp-Q7Q zDfApuBD!57I<_(b*4DC0KY@YG$iSX2`9vn!^^}C0yl{=z3jnTxatIdzY~JZf+?!@% z`H~~o!*FEzN3g^km3k@9u5OlaYwLRE;<v01#5rNZy>}7>=>f8UeF1k;XN|LekB0E+ zP>js$);x>IKFxeMI3qjD{KTPNx0h~A8$kzJ&3<8mehPT@fS~Y7|I7&r`gR<Dy=V<| z!du$6+)<P3PwIN<9!pu1h@UrSXhr=v$&F9oBJ&bVS`hsehHbUE8JY3wyLpI}+Hhf+ z2|67c(bH=jBy~%~I!LP{4Z4kZ&A(<K!;h-gtNid?1h&2{16%!`{U1jZ^x>!;p~nJ4 ztOiS$I99ygQ?sd5T{==lRCDFil|5$kUS>}|!NrZmQabjA^|Qo8qLx^kP(DV{+A!Sz zwhiZ>L7=v`v9+(x*!x}G@oO(Q<wHp2!d^HohWV*AH5ra9K>*0T?-9QoUMB?ZLlRex zjA~tl!woxS!5<;-m&o!CB?gM2x>YBF7TJvc1B}VIiLiy1rdp;7YrGOiZnMn*rwxt0 zqndE(X}UeyZo9OZ9ozA}#14)$>A)OW;>h?M5cXFE^xe5J1)@-Q%SP#6)d&iR4b^%W zT*s17aFdYm@pZbdJkHcjw3cFlZgy2(&qoeYaKX>b(s-W7^H90Ogpko=Si<&T*WL@n z`8NLJzpOk+E|<%0<~qc7r~4D~hXjoa@iI|D%3R!A#+Z|fSFn(*ZsV?aSN(f6`<%#o zZatX7_xc`K2*vcu3AY`L1Tg@tsay8`CYY$NfE6*mQ{maaBYH%ZJ1T14V;u$d+liQ9 zd5=S2I;!hNfSR;Ri-tedmnd0P$+(&(Qar0wgzjE-Wr&{D_~;Tzs3Sb6%4n3<BTK}X zSb|n{3l1=ccsD*CbsY|H``GC;1mJt9%S07bC_(bm18PBa8vURz>iY>^g;RF^h}}G{ zcI}(UjWalUf^E_IB^NPNQa_UpNdplRWI(6IN%i0gPdxKR4TAl$X2ir9MG1UOU8a^K z?1iu6H>Dqv-MNfOSjLEw@CcF%!oDw<T6`R-SRhhOs~{+S;iKZGJFuo`z7OWIpHLna zjnFBKYFcI1EC}l{J~AaHp|F&t=D~GzsA5*^6z8N42$7$U3-05yC)zR^tR}JVCdcGz zTL~Tq4_(7VDf#B$Mlr&=<}}OX+J3)ai*ltkb|-{mSm1K3)R+%N0n@^lYKlK;cc*+b z=hto3h*t4U43UpWt>tMskyDS`BbAWTiq%GAXd#5e0IA}cXUI)4(CSxuam2czUz`=& zFQrzH?lv`5jYI@MqoW6|?NwC*{>%6DU9JZB@+`9bb=k(|NDad)`lFG+liYUJL$et= z^EN@}ydoGG!}KLRjj~V&FV_(YzOjrG`y)4vZofJnBOC*b3K1PY)q=T%+fV6xv8r}l z#&pc`Fn~PBuGLJgih<IhkTxipZ`;Mh;;+~t|5CctzY71O4-K;tVG<r($wA_c`~eQd z@i12PtMZvs<aMdi&e+IrHS07*bFOd6#lx;i5sBVbD~6#8DK%d^#48wF<?v_Z)FZ{f znp7@!Z9#kkX{J(4Lmhb!?w>xHqaUBS)Bm(UBK>-vi_QMeq=*y*IME!yw|jsi2fb8z z-V{5uQMn+ee+FEsCpsetFAG@dZCDfsnB&!8DrkJ?A;NZ<|Anb78M(tTp2Ve7Xw}Sd zl0hjGD32MeJx+H9l+p<eyKxsbMb5tIB~(}Xrw?3{e%Q{MY(#WjNb<~Le04U8==wH< zvW&4xr{(!%<*gCca21msMq|&kQ515jt;*t}<69{3i_T_Gg%P3gP##G~{1>rgR4T1$ zrmg%=Y8$V;8s-Cn&uUv(zrkxJyQwCm75MoQ0!gfO7H--vV@`7i6|-rIrJ0Q$w4cfC z`cSE7*V30T@}$UZD3%~RR<71Je2}%3&&l`=KWi#80bs6-kNs_iZIzDU1d6$tqB6rx zJ(Bwa_R}sDOH-D8ToF$E*L2|;K7^16i<i<=bNxGPG9Vu&8D<%}`g*}7;IF8X)6#4s z^$d5i=|sC7Gx=013GX!b$f`z4jn7RqHIxzPPxh5r#R%V4Da_}K>d>_{RS`!qQo<10 zegD<`%21(qOSAf!UDixWfTB@}4SOlLjw-Gt*3j3>X0H<tnWxlMX(=#6qQy!z1@)1p zsjC#53Tmy}EM){cwK(2tBQ9d&cw@hNHD&D*tw*LC*kOEhgNiX%9l}y@9(xq9NB+Bl zmxeOLrT8-KA02b5dFUAf@m-t)1RXJJ2W|_o@`zG4dZ`C<a6YW5#wx=I%)-fF9P4D( zGnCP`tth*$(MDMLvok3e4PeU<0;`%k=J(h~`)qdaZ84BC^JuRnM4#a?zEIP#nUNcc zp3@}2rn6pmH6jlUa%qtf&HG%T>b@MsVzY`>U64Ypxg<R)++{t37XN)>bTK`L-(59L zouv&ARl-P^zLplRxPM8cJ5JptjSVUxMJ$dcCMihGkidMrZ0MU*i5H8zQh9Ku%TUx^ z3}g%t^oP#Z8SfhV{kr#^<aO!!4ad>_gRz=3#0{y3GiI@NnSfFM2-Wjjh6F5g)L=<7 zjX@ziaMZ{zbRguD@_|A~ynV|HHQ#ZoG5H*lx(F=9L?aA}+W=mH`tZVWj@;Kj9E-#J z(bItLnEb5tv|pC$v^0{@3+7}1i2Q9Gimt^kJr>rpNt3Inxgt@GOlOU?Z8+^ux<*L+ zu978e4Ue~+SqjqyxSpV`73p<%F#YjbLH8?cGa1(HVHtTdgJk4QEtqLhbMmvXE#(R- zHdG`G5v+-L$LTwgID8AcO*ur%$sY@TWfPm`aaiw{L|enz&bt4B221CJG(xw9B85Qt z6^;l-FIs*2Sb9xeI#CMJyf4jWPc)#$!c8#G(b_e^tKjQ!;SAr<FIv&*4)t%CGmOfW z(bO2Lod?}a&#JoOb2LCg+trU#)ASB0VxAd&%j{Ndif0mP6BC<nqVssI?`^%U?oYg} zMuDtKAIvp49=IK=HSM_NI2U4lr@T*7mCw{sj65B3vH!Gn=Cn<^d0STH>8hFKqS|l+ zn?x%b7<mC9sC!2sk={PzkItBkcEk4Ztll%g5TU1F3R-U|(|4dBqQVR4+#w4cEj~us z=zT26DP$YtZ_*dAoOb8FoQ`{rfi(X<Ua*1tW~W|wJ^Hz?)3agCiw&jYbgK|*N>WH9 znLD}yJ9qg1uL2;J;rz_bYrx4!7^iGI)@j6KVPtvY8X4;Z(DI@isi9#_%~%1m>rv5d z-_5dpWL>&QXUPuhc}(_Gy-S}|NUV>OI+#`G6cDf&+O&Ki#TnGlawxioOl%==)WKBe z+KgUC3r<o%8a(TZt>5#s)}zmJoR$vp<xsr8+~G4M{r_gf|H=pw9-{SYk-%ujcJJiu zr2HF!(GbqeB0*c`E>T_!rieknr=l|}E%|8cIp)uuzS;FXK?`CPh{t)a7d;b4vk3Fv zkhoUSZjX!Yu{OxIfN}|)?;(c-;F!2LFzl9h0{yQQgmaSBcv-Uh5BB1HZJ<k4fggJI z5^ITnW=H%ai!CAYpE^zg%!>U%Y|T{fu#mzk>6Veo(gHE0Mf^H^P6tY#eZ*rKJ3c$x zbvE5H8!}TUt{)9<3_JNMpH5db@y`$@-}B2OF`^M}a>&Go>Ox`Hk-|Iagx`GB%+(d= zTVH4X)<HOhXN<t-^h<B3t3XJ;O`KZ3C+}wkuY~x&&OX(!US>w^V{3L`boxgX2Q{_i zG1nl-K7{O(WynT_uJ31|q~TnDOGO3Mt>qOB)P7J>f0JBO=2_*j^FuoqF|IROoA?`{ z*U^yr_Eacy2)ks*bLO4xHMwOtXEDF#Sn^=?JTvM2QBZJlztA2S>}~`}ijeh&=mrD( z)a>Iwd6mv*)YsS}K(a!3z3G-y_k2oM`_zUj{G<MLqeTwu$|={F*J$th$XWIJ&lws@ z7Fsl`aLg@8ahTNU`d?4GYTS_bQHIp~NM$sA0BqDv!*@;C%XA*@pLd8EFUa><MJsq9 zi<qK@Q9ThtMlU5z=`ew?1%_D0Ffc1N7urL8PmX=J7OPj2uB3=b2x9?2T=Lho5&km; zVEV5QX%@M05j1uVLk@_}HRqhk2fvTR9QvSewjnggM~C}(Y;NqjS@#6QOb$yDJ3VvZ zdB{GEZ5~69vjwcgPY0?ZG;|&LE=IG$q={|#0BlvkXvj1rqhsf7<NoY<>KpHtmguk~ zE6f?53&TOjJ7J&j5dmNZ@mL<2fV(*R3wZT*l15a!t4ou!66$gJ?&{0Gp6iTj4}OVi z`|)i{P?}`XXy5=qBJl@9vaI{SeznaPkxbAnqpkM_MSj5BYjfv?A$=~}<)3;sSbEYy zj#+&Bhg8(p7VS~;b6FHyvj5=IxB11@fhC24u&_s;>(umXXGphP_Ezy~H3E_Dc+UF9 z`p)*rd}5t$bif504E1yCIA0uhl<BtET``>4j;e!wM2z^l^8Okm7YIU_pQtV!_@iXI zmC@uRJiSzt@^9B`?v^^LyK?z3?$5@b`Mynjk<D4y?BJ87z^2W+-v(1L(xY)<*A7(( zQ+k8nQv^|DC$O8q_YJEMI+BIh(eW_K2-5X?E1F_7L!9wC2GqdV6s^B|LEo208@fVE zsn4u+i=)JJK5`@f=pZVYF#L*7BzI4Pu<-piz!dBURiYeG@}df_BC(bU)NT`MMs7K} zTcg@NMMLKB#Wr)ysW<Fo>a1Va1P*^#iaW+ANz4!S`~!&U7;eTt!a&;BuXHPi(PBO8 zcAtBWTY#a_-w5Lfq=5$D12mC$9DhNwyI6!EYby9y<Pdk7CV&jia4JW~x>I{72LUP3 zBES|g7l<wR@iWR1Hjo5NcO-T=Nr5yYcCE$s^z?kR-51zWmn@658hpVvzJVwIBP^2x zlsa#+TOietKz`t!wFv_HM!mTW@8QZY8i98)?0@todNE7wt6KLdQ$?b~)T3vxPBK`& z<<3+bX(X^kHq97}r76jr)kVR58R;`zZvl1rIBBuq*QHFBe7Q1z-mm%YHXhrlDc4q% zvPUx5=Jl!Jsw9MUGH314LiLs*<D6;KY~)!R`|f3t;IV6=;zxmtE!Upi&ns~}N;)FL zeY|?4=0S59m)-sK+yV=EywuE3lw(m^m5-|??LW@4H}Y3P=56yK+vM*-a$b?ubO0&1 z#9JY=#`qwb7%lY03KVR2!w2X<3LGrxi!Yj-MN<vCPsNUJ<mK@E{*Y(}@Y4H2`ceM- z1f=<TeY^OJfnDNS`qeeoP=X_Em_~Bi+w5p*U@?LQeWZGh@>QG=IG;eNm4GXogT9y8 zgc<LniU{yLZ9221v?v99g#|mbvqKc}x!O6!i$96is{3oNRk(NF1|N(+P_5nLRgn$C zB$M(_FQPoVVtv~V8<66})|3-)xA;!eu8<8$uou?CV@zbC(aq$WFc!hBwErzHzvrQ$ z0t8WcV6Oxf@q{|-OZ@P?x^*n$G=SkT8C{A5sS$mnv6D$-J7-{|RAZQ?UB5rO!2G|O z&O4my|NY};NOtx*ICfcy3P*@@l(I5HMv|QovK>NFGBUHGV<lV2I3y!GqsVcHW3R&z zj+Ni*^!fh$=`t@ap6~m9-j4^27V-jOn2<9KY6&7guHD)jgt|N}HWivhTD~AHXU#6( zJal1`(+;7a>d(=dY60z)&o}7I0*Y1OA7RcZ{HgoDZ!yF)lP^e*n{507)`2WUjs(kW z*QO{B_)C{~sDOU#nf>t*U23f;VS&AbM^tsPqbaZ0M|ucZZTcN}>aL|_>E9akD00Yt zxe`%u3wW;<`OBL&S06fvG-b;rsUSY*n<SDq6)@8Ywr|m7DkWS(Q38xfA>}Z1AyJmM z^A&+TbS#Ccf63{`Yf3^pbpUH+bK_Lt%O%}G3F)pRh89W!uhJvkOs<ooUZJziQvJ)S z`s=)6N)Kw{K=AuuAaVAG4s5p)&&T}3tL%e0`zHpk4yi3VF|vJl%VbDOe50{Os;~vx zGsyO?W}Dmf0>{&{mAdzSn)d0xu6Ect-KG}h>r=|vPgKC5`%In|?pgmzgW|3GIsPZ8 z&~j^|%AegHxC}c#+94r4mVI_4`c^byECCHJV-xOEbDvL;RT<>BspA<jWtrSgN>ljo zO-`%PZEhh+wWwphAAWO}^IzbWdS+v3tS54}pUcKDrV;xJBW8jwi(L<wOVRkvC?M}5 ztJo!5K*Kqg{1{^(Q4%{>ASme-+x}ir=>=h5vZ5lu^6EbZ%4`&F@>`TXWOhgO-8VSf z4;FB6FJI&lklu?-P~G&lDjC0H+{|Am^1~-V2VFKnZ~o;$#5zP~Gs^sT5V=&!-+v!O zNJ#<kMml<f&ui=HVia=?&GK2sKSi|h^F7YOHyh2Lux`D05aslF&%6#QCEYc^=w@SM zHJ3V`rh56~zsH}HyjsPVOCtIC?cpUZ`GdY=4CGIApT{RQ&7%_1hiiWuD<5i?YseVZ z?i?PPmRsKsbN+WGDr1{Dn5@v`mA7L8bIAeXPvu1x?)}Not!fLeLLsAzg4};T=@Jl` zo|`utg9E0j|8SC?ha?^`gwOOBPE0*|pKhSRk_Q|?x0{1eB100>-D8fFDPyxY`>Z0a z{O|@3=oDM-+zS}>>WQ4Bv)Yj#7drta!U;fCxL12=1w9$2J597AjRuWNRk#R<yqnc$ z!;h%Mk#TX25ytZB+v+n%t;1n0c=c_ujp69I`iFb0?Bb;pJ1m)X_A>~&Hyh9ES_v+C zmF@ma{YV`19Sy|O5W?WY_r#`F;#DIF!tX(wgB|doBF*WW7tvpn7JDW#JM|_{k+h58 z1a5PA8@4lk3taRsxJErd&+cI!zos2qL1<DZMXCu_VG|O>bj7!mOv$RF%3hHYe#bc| zeP$r8A#g#8(O$ukHcN9h|2z6Kvy9}MfQEEKv@l6=N1o=do6fX}^fcp@a^VD~oZawI ztv}HjP?CXaZ)0t7%~;^F?w^;|@}pZNCBfsdzW{U*VwvDZ9xTKA`1JR7#O*qV8HB2M zfT=&6)FbO_KR|B^$l!ta!D*yCW;zeK_^>H|Hj&^bR6^4bBKfnvxgoGYFhI!$^zrWG zau!P5z@@bkepDF73CF~d-LLqw05izlm;fk5$wy55F9+Y3JaBW1IMml}?U0wZ@?8+S z7W_w|tikpwSlk*P1<=1)iW!LhA4FjI?+Km&t#)%3*O$9U&++{)B@NL+j304^8Fua3 z+YZTNvhB{3;N_$E^rNS*#2xh}8iv+n&@n*iYEVX*BvK3LDnaLF&3kajA5X>3;WTp8 zV`5PnSGt~OpSeNf06rl*D#INqaeqJDm6(I$GrHi`5a;!ZCel-7W3g1pE9`ndDLu2c z2JT7?)&NY)Vm9}JPhYCc<)NymquQ|3f9S}e-X~a~%qo-LRX<rbU0qu%>drlutS>Dq zOJU@PX@Wzh!lm1mDk^E;0p{_0d!16~ou+=SDUv(PbLHBbB}ogHIeD@D)*YGLXU*>_ zjP3w}<nC_n{8mveruP8(T<|~$jOz?rUto48|Apfe#aza>)u$l)I%W4fKbB~!z(e`3 z`E=CoUnyIOJ^VSkqg2o2PMVGCSss3tbyDfQ9u)l??gW654d<Xb+~5`I?$@sY)ri^f zQ~XD6=?itV$C#;wi%98lgpDCi$<{9Y@?q&8J1P1K*;jG#>~S=7uJ^w8FQt=d?|RSF zx7v#p?yBCK)1s7Y^BuRgM@(u_1yQA*OQ&<`gS~J3#aZG`;yq?L7KZmsruKWOfThhU z9Z0Hq8!CPyVExWygF`=_s>~}NyGuB?=lgUNENeE|#N1S$Bm^R&HvhIS-JS{Uh>QY1 zG=bS_^-{!Z^L$0QZ_XO#P`uk$v4DIjs_z2aM~^MIM_E0VaQ3R>?*%E$l9YJK<Qq3c zlt)M<u*R9-%s>WiN+DpxSQx9TjO#&U3SEF)LOk8tG88j|Tm*{s!a>oOR|PuGps1Iv zsv{eNrXLWZ6%2JWTE2eO%bE`BTrf65B2~**ECzfpT!u2j$7X(>ol8S6J^f37XSjts zeY*H+66-oSi5Yh!_z*51*4TyII5+I^=aVbrH%{cz#)qOJ%5e#G59PNd{g@p(U%N<~ zL9PB+m~k50&E{2_xVSiN-23~K!j%lK5_R}sK8n>|@@!0QmY!}lV*+7r8tBQZu%rmS zSzRYw*KUp2m-LrCZZc#P^mC$JGkQAxEgc~hX)qm}9p#&<W&g;duk#}39`Jrh=sw$J zSd9>W2W8-`&)goJ9v)<Q_Uwmf-h+p--4^g%AqWHx5xla;)$W6VKhF0ETOvQC3B%Vs zlzm0PO>V~{z`f|Yjy|)(62nQ$N2{L}z;Q*8BF~wBaVM&99c0AAs6etYYUlHRuTS^C zpRX1Y)yf8<2Hi>Ta0Prb?!6x0uvjhi!;iA5<#_@qa`}!W8WjC}S=pzkBXWiMjfHG0 z5~D~iV@-ZV+rq4LYoHbORKI{Qkm48-s3|AsU$}^m@gR%53S5ata{J-A+_0zX%q}b> zYTjLW=bRtgY$u8^d5s~D5P7=RgD&3Uo`f3qzM8owh|GD;R=u0vGG2So&ZJkPMQpk; z?MxX{8deWG`AfG}m(ke$qJ7Y-Z+tT}OcSoA6!|JVrs`q;YK2-IRPZ;iSq(M9(=e@; z9m+LCb}~uOt0fphy-4ufPO0AA3oSQ@_`ho&-$|*l<n6LUyZzlK@>j{}Sb9>K-^~&; z-JWCM<g_3o-n+%{<;zvbh-|Ve<qAh#Ar-wtIF(R^RT_Q$Z6vdlkbQ#RERQn^3|QUk zFb~q?IV)S^7TP^2EA}MU0w6XE=F_n~2XT#1+biAEuS9p4MJ=JU$-t0RsMSU`vAny> zBuB@F48FLg+KP(Q%VQpU8ClF_Uhw$t;(n;45%pJEK_$EAanKm7e!8n68@Qa)6BgJD zq&-~}41I|;mpE_y1!;)YPyG)2g`qqftkO~Zr!W4ksm7!pM$B%nYr-$9uR%21YK^Qo zL{a&&{I6>&*L2v}hIatBy!3B`(cQ>EaT_v}mqFS~HC~i0<h}u&y>BHJUK}OFtz|*n zYZp#wg6(>NJa^6vq&UvTD5>!q2rzp>v;}_~y^8VF<$1?i`K!}M)D{?KBTDx^Rk-^~ z+TJ=|9k`=sn29`Vq<Fitu4{)*oysNBhR^qX#Q9_b`Ql>FWbADo3cu9zH8iC6)HFP0 zW{3VRcj~vCT{c#iA{cTWH8(J|a2-_Re8~Q_KjeLh$3X{+zaAvXMF|4OX9tU`(nn${ zZ9(%Msm%f0nsMGwvol<dgW*^=?YsT-6|S_)n8f?P8vZ33hx|xvTF6G%b8B3P(Usx= zed1rwR+MzSe+iWyY2X{%YjCrA!T46AlI=?3c}5q20>uaafGut`TO*p2BS->WO+&_q zcXo9@8xaR0$Cz6cUEU&76pCfL+NuA;Z%5^O&tDAe<H0C*bRP#y#F^bF29-}_6NQ34 zkEsjuzwaV1ooDLvcv0Z_1`8=|4&N&7`nJ$?SX0GATLURT4FL}JRhH^B+#Y~a&whnZ zP{Mv+v+DetJmD0)`@tlbv4Tj{I)1NCWa-B4uBZfj^;#EvF|g`j?2Gy}jvWYse;75@ z79gyb+#k4`(`#5@6FSz+8~aCfi|1FLyjHISs8kk*WPvR)i94hPj~^Yzg(#w$I3*T^ zey<2bx_Ck+5!{(x_HVqVMMjt<I>;eL-c~Jv4&Dr!zWbc&-Eg=2KzOBm>7d<fca@#m znLYO01>LmL@LsXL{Rn)Qbak;8uiVw>=@5SRB$Iw3EN(p9VC{-SXd+I(t}Q)1{T&#A z`aE)q0k`{9>y&gOq7&oTuQ%wDD#1Szg~aOcjTe<d$|wtpbvnA0)DA74GlVn3(Y05W zT0{p7^S(iHj6xP+dwi}#Uo=OoKB3y<|9=P=Eq!>w;cl)YrPvpH92+WK&F8|k_`<Vp zXzWvOq<A(!YTscvw>1-l=P%lO?PpP2c`*Q;c>ee8o$&#<o*ogWM))Wq_8wrQS2x!B ztm<noK%xX2>Um``Z9w%GIyhCz0ZTg)+pkQ;i_mKKMNm&K$=GWluQb{C@gyo~w1_(| zei+)=*ocmFe+Q6mbB7@AWb8;c+z0QR^SE*Q5PGqG=+9GZCnq2&S<l(u=%Cmfy)B5& z5q(73<a(^~r?kc#jbJd{zWs{Pa2==MVAY-Rsq^<*RR7K!16L`#jR;mhCoomXs`2$# zCT`y~Ymq8fSJwg{2UXbJ-5u4?;!E?BnN`?QBmVlCwww^4BW-$NDO7mFCIoM{8u~=t zJ#Z!(#hAR?{8{j;PVDPLsZNbIrxk*M9mxUQCqTVXd-SQLaM0lo;97rPCs{04gEOrs z(Qhy>DzO##g&hI5(EeJJRqY($H2j{Y1y}HD(#ue>#%3=nk3-a#@p|OAjP9z$z|@yD z@9n9dl&f$yjZ@?r+%SxJTTkxl<Rrlyk~+yMG(>BFH5gOU8R%wjiwql%a!uO8R&2<# zRwd|kcMU*Cvm$3m)#GinN7(Z-vMRSN_tfCPwU{Fc%K18ba+TT6q=6ezh#U(Z&V}2= z&afggbf6Ab-qJN~PL`HfhxZ(YpYB0sYSzk|qVXBU=<A_jfE&&6L-<DiGo`s-S_DeX z(Z`_>K9~zsTiQ&4JNzcT(X~Q%^qMp3!W0eaYdj??u}V3!vRnKX`oRm%uJIO|M)5a4 z#gda==l-H8m!X}RK*Qd6C{0toEBlm!KK;g2(x-Pw!z5U6d2zxSK^?<Ers=)JFX%uq z;`)umDc<rLSWT9g<q69@Nzos{>8m;rWEYP?-Ri*1*TzHLJpfeOL;K>aln{Czr@Ey1 z+*0d6<g=Ljr(ay1rLJ54^eMYDSzTEc2hvR8U_tha*fSgVTqdJD$3%9+_4pMZ(l4*! z9a$$Hh29X}bRJz)GUh<~+%MJW*!w<n+U~ew_hjh0gd>l!;iaVm%%5Mj7z4qqg}lWR z<iY{5cc*qON%nq-VHoIxJp0#G+R;ETLdl6H_1o3E!%$~%xIKuBoK4$y*|B@3*qsw& zT2X$S=K1+LHvRj0Yak7q#gq8q`nxSANBHF8NjVwe7uMf8TyW70dimzPQA_N%s7dDp zZYY@lp<dFNwh7I@U!!jz-nvlOf0%Zw<KWRGcV?nxh3Z7)5cx&~nA!MLa8EkIQOMuO zHe_;}!1;p;Q06Zt+Zli#4i+TVB38*t$a6yX9BXsvFfo!i=;>^5gJzS_SgYNMsOsux znAR3bwWEbd7<_e}Ww+IdR<u1~5zzIfe!@f&wU3m6f_D^j$n_8`(GB~VX#Ut281Rc5 zEG3~+iBvtcl{%4PBH@8~PLm#qn(aLsvW!b~{K%AL_-yl@_IU4tqKV$yC7*SFK}=ZH z;WnrW0QLe~bzzr|MOE`9U3y`NG+OS<WSwk&ewA)ou_!qLBpz<<v#E33)rkq^<_=Lo z7xsTJmTIeRVV@JZard5GpJYo=drZd7M4JX{a%L98#nP?t{o&oSEEZI<x|07mb(&{| z_mD=|AMc@)Bv%~!UJx=~IeC88T$@h!-lv?zgsj<FJDBEKfD@xfG@Hjdv<sC*B-1Sk ze@UuM(rGuGP7u<kO;Z4k@MNJSzvD0IF&e_r4Q+bV5btdE;(Y^|k@tG@DTD$nt(LlH zvLC|IP<AdKjoQ~b9C!VO(G-zP4icgA{lMC!`WquL)8lQ|pz+|P^fwVJ^p#JiQgPtq ztkYDzkySzE&YT08u7#*=&C3;LIotkXA^ktuz0E`eE!j0y^(sL_iJKZ$ies0qw{2nG z&~csqp5QLgF)NE@SZ-qAZhAA?j*Ucw;*a)DKJtYA4r1~?{tSsS=~r{3NQI*aR(F!u zavq@HGd`-FgTcwnl(UyUniw9D^)%X+9DKSKoC=2X1HK~J^_3>MdHKRwY>(L~VmAh@ zZy!>c6c<Nm=l7lYdGz4ulYh#*`e~YtoeJnLHhvSi*Ew<swRd4Mjk#u^@n{yaJPPkv ze}D4ZghHt)y_}Q@ZZMlv7LR~=po80ylkQPRIm4=$VWt!aTy1U69AU&M16ntrn5XtU zq@0YO6u0$%CxG>~t8Q0=+&{Vej>^q1?_aW56{K8`uQGT!pJSiH?m>OCQA%s06!Ma5 zZ{c(HR$1`f?V^AAWI_0yD&qp?v?|&4thSI#F7hg?+{`MRPS*Myy)kfcf%Kpqutj%e z%HKr&4#VXQnpbuQrv%+k&?!fHv89zyj8y+dDZSyYIMK?bAM1RjFQ=t62G}Yq&%r;S z$&K!0{rfbr!9<jKXRtd*U(vVxMYUN@HiyH#qKLQhqW3$_M6AC4?~QII;P)i;EA#wp zQ}PPw()Eh~H>}d@ZFy)d&5OsnsQ<ngS}BF<{lqG0*<gz`X-Q5k@I(^s={JptEBR;E z0jG+ZbmRy-f^gTt>+E!4&4G%DKUYKEV1+`0G!B(lOsk5)p{#814ij3tb*J@|!+mC3 zih=Rm$VNSO?)LK#2=xTw-&G!8rFYiB-!GDDvm)>bXX!feb|vkJeA*zmFhia4$cZ7l z%*cV+BkcL|dkNB#(hkan#m`L=IIQBEHlOVUteX%_!ngpimMuhlWn$YJ##pjr)u~F= z{Oc=Dt$C*J(G=tk@mY5eSi>UIg#_m5*$UhsGG22R1<4LbOP%I>^=^Z&HsVO@<Fipr z`5N9&Q+qJMmLU;f^a#r1tv1^x!gM<?ly|I*C0sbuZyf3~j5N_OjanWtZLy3yMn@y^ z()HU?wI)0*D=V5Xi1t@hpw`Gk*liFp792v+47bA)j`nt;b`TecSE7$}jPAJ9V0>*1 z92S*N<}i?9w|V?=zEtslh#40?jYM~5&0d?7#QXf*c5cC0;NbMXC8oT*Zi_e#@wn^+ z|J`9tb^#YlpE|lcapj`LM5^ixK=!w0WCa!%6h!&qL$mk5cw$32(j8=ys+$G%^|_`M z*19vtUp<CFE`gO$8c`<F0;zLGk8@E^P3ohw)$p0k@qJJJqnWF1zSnz^ejB0g6^TxK z^6nwyS$I3T8-QRNNE*VE)}THO<zk*Ta5(&)5ykF!<0sdZ_|y4TPaDsQ4_LG}5_Z9I zGJ{u^_uja-P37mk>VjJX#1rORW3H`}ZmyYetQOdi3cuk9`PCA{#8xjJdVD_q$rMdu zjF2G8nYQ~VA_!T_^<1KU;^eJVPN<*s29`7N8{p$6ZK=1MRxG#R!~q1jKJ#tnN`_;Y z4)|BQ;wanPKjYNA10(I6pBS^L{q4{%St>gj`}y;x`oT&KkcMcG<~*QY=!EH0DXutu zXr!~B>EXCqP8D);QrMjB0|=*9i_^-D2geBk&x7<n-yXO<X194l%2zw8N?u2P>r<UT zegmIGD(N0>Z_`aWY+l_wir!6wZm8`|)5!=Pc%pGXuv6-&Zm(+z9)_Yr#PwzK#mQ{@ zs*{8e_ae4DgekA~SsGuBHo|Wa^MEA<=Knn-4!(}thI9GjiC%yGdsyOG+4PL<MY`=D z+-nJB4>Je9mc1X?gRT5o6$F)zCuzQrw!BYJGbfX4!(RK=cf_G?;P`we@=U%wmX3R# zzJq!<`1p0ayb$H}k8(YB6v=ypt{yN9?IB*#yidhSH)np^EyRv@CVnVv(Tte$PQ5Sn z;zDa+VyauntZ1qLxJG@|-3CnGcerRE$^K`KW&)mDSXdH6pS?pixP^FGYPwvXvgD}s zoY8ozfvCq%^bD~-eC}RsTJb<Le!K4A!)?XUwuS^cwk3!G7cZBxQ<0jf1D_B>d(?!8 zS`K%whH5#>Yq@|_+DrxlwWd#h!bc{nnv545n<BJl#yGA-Tp)))>ci&6#FYHSoAijc zFi*V&#DwMZzMPYxDhtrK#3qoyuG$Jpxy1*=HRH@>onxalxj4hzTCLR&=Yhn$_Ccq& zSR%v%a??qD^5B->3GEwbEw4KU4DJH)6JCwSA!%NRUZaELVlccn3<W>ZDE#injA&}X zXLzIZlH*~zhb62Ca`Y4b%@nu6$843}mk|pWUK^u`r{s0IWoot-*hggGMOXXIjdD%f Gi2nm4_aQw1 literal 0 HcmV?d00001 diff --git a/apps/meteor/public/images/engagement.png b/apps/meteor/public/images/engagement.png new file mode 100644 index 0000000000000000000000000000000000000000..2a60e01c5e376122c4b044b2bcb3d041e2f82f1e GIT binary patch literal 42933 zcmX6^byQnV6UE&%E$+pGQ``xz1&Ujd7BBAZPH`*luEpKm-Q9~@;7fnsA35*jotMq- z?7o?~ckYBKD@voId_;kQf<l)8NvJ|W!5Bh8K|>)SK<?nVj1WRD$abKwKcS#dasPdw zq0%w{kekpyRi(wCDkq4KAz$FkMHNJ$plV`JUyb0Qpo(N=Bt+F+q0ifqGpMDVXy0l} zpF3I)g}CxQI~p8`Ao=m4feiirpb@kVa95|?)e5%_-0%E|vbG|q)2~Kf@_k~BZ|<iM zfyqJ51btkevAB4-I6i)<EPWk}C?9hFfFGASpSi3jcq3F&RLV|8MM)L&xZ}a9&nw=W z$1;Xhc`9Y2)=Bbrae5j74I!Z0@DmyuM!+ubb?c_VFYhuX+#{h^o0Awoq_z|-19W?d z_2#@S;}gy#vhc@$j|2VgNkD~;Vue1Mo%7tZ29ZIbAQ&?uzoS)!;}OhwFT7quAOH<P z?CwJgPN5GEc)+9wj{_lZ0ZX>)Rt4S*cvM8MrzQ0NKTdfjnB{KJJD(l?m;f9Q6Z-@` zYTCB`Jg(Gfl2+T^@rso~O8akyz)x0h`bMVV@vQ6b5s0smLH=4OfQCUtTN|*;ljxA? zZAdQCe<La%E&P&&*$-~%sXpn~Dh{>w&*%B`zK&jqUWm-6R!au>H<<kI0593Xw)6T; zXyMF_!z+2vfS5RoRZ+|G9k!I)9JCN}#7Dm?q#($n0NU8f{5FN3=6}7#$-iX`8=^^7 zP#P2Do2Yk;#McO35Q4`s;r|;m)=PG%W2?xS_}DhQpAw4EFcLmcR~;rHAkR9jdYGd2 zHJ$9g8Ny+6tCTs2VEWY?88JX?EL{wv@%gzJExO1sjfL2;crWDNaDc?(+ZP7RBPZ0z z3Nqt&R>%ac=sewb)4|;tp@!xEjTG1`F)-)Mr#7=kHIPIwAm*Rwn<^hen<%%kPSr() z`_FDmhA5&c<9x9Un2q&70-SOoL!*fs{OwO&#O*0X@EnkBqQWq%^_TqxAKYF1O`;OM z#PAxnE=CMbH*dLC`xd>`?)w}(-fGrrc1S*ujSZR3{iW?PAz{j+7V`j>&RwY|TF-+# zd$M=9Lyd#Bf+{ah`4gTmWcmc`csZq<9BGL60#oKp5)u>soT4$hkwC;=j@9e3{(CAn ziyscZ?JWr|Jk3xRm?nDKS>?OSCM!bxhaNQf3%8>P7kd}P;Q{H+IYs?`uym+bCpV`# z6y|i^y;M|JQ&gS)o6Ua_cs(DBsSPtp&Z}+TMYY<$2bJG!Qw<vShwlTqxe=%+!6#oB zw;#jwWs(-k_2H$XNfY!DNvy4b%#G-17~NlgXbf|Lt|qiw`EJ*uNrWk`d09q1?w=~+ zMWP}j#WXbuIeECcHoOoGpRT_F>5+DZjsW#d4I}Of^70}k;(4G&iqqBUX=pGQT&+6S z&~yoy+dnaUs-@|IY#%E0fZ-+-eoo9>S5_iq_oS`h<+~-C<r$KP3$yXOA0MgrB*tSf z3AWTW$%5=fTRI#rc|E~?$h_um1Rb7}aPox2e+e@eufNG{KmOGh)koNGNm3HL5x@(F zcirx{zM5&wWFMc~!xI3H{hZlEmmy$K@PK0za|j;9z(5PXlr|bonC$vZZrFN%7G%BZ zBFm2N^yXFcyq%$7-n%0ycvUOM8sAsh&>;EiJ(v1tbb?=|7|Cj#i*S8Ieb;?vE6sLK z%f(tdqQ0kPD|HB~^(xD0i0sBhrU$jrt_np!w-yo*n_lM~o7(g>+Js40U7-u_wxox< zC$SF+e!hxC`o``^UFDND)MADjB6AeR+T9vnefXr47<Q|LH3mV$=c(BwKNr1qC=bQG z?S9zl16gXJNz~f;*;yJN9v&_f{BqnRrmtvanpJ^T+^8=Y#DO=H{Dg|g@y<t6*rGOt zrhK2FMKiQnk`qkJI&w(eTlkIal`ySV>>8DG6=lTm+-)i<DBs27=h`1<Fj6%&%@OD3 z=X+~R=zdpJ^xJmY#5H~2%(}3V>A9YrRb|$xb&bou$QUQu>gw(Va&e(U-mV%fQH9t- zJ3O=ju@DYU0quFLo;_DvS(E3D<!J;C_%z}ONy5qvUCj0X$t5=?R*)u1q++vObrm`C zl3#*kvzDh&jmO8}0X^3O^6y5pAZlrl>cGH&(Q&-w)Z!v$R1~_u1v*HLEWS#}XhEmZ zG)%b^vbrz7%-VaUr~8rvzR4x`TnN78Gc2skprPO00hy7eXA0!<Ki4S_^_C_wQmdfA zrm+<j78(WtxiHYtQQ4Q66WPY942lhz6%-uUZb(JX+?BEdt!Nn)Vu~cz`Bql9W0f~! zhm10{B?OgI%LoT+k>k4!=lBp1LSQ8#C|RbB#RjxOLlwG;XJ|0zKq(#Bq?*sP;4($J z-zN>`mqICN6Q~?JQld*{OrO{RXagURsSWW|a64`t-9Kh|O=^7#Jr7ip{=|tz8f5u? z{0jUn)~J&5@dLz^Y#!<iM$-pUE0@40V<u_|S?-p)JeloqlcYT;w||bTT2!*O$P4*g zG5492Tb!NXV>wES(5ysqz}-H1PjvQq@m|(p(Qlvn=iOwdZ(E7!r~f<*pdrHHo$~z- zY{p3I<Q7)pg$ePvL4i}C$50@DTUdUo&D*jNmWu!fvWJp7nc;2tos)bQ#NV0ti9DRD zX1_6k#wAM9nNmI32yjZX;KM>&hONzpm&g1~A;D|lIHWrVo3Vubt3glfFmD%eWR0>R zkEnlBWm{$7)24mvBPa8K-Z<>PNEgZRA@Qxhuo_Q=gQ8u)w_6KX&A0s$fo$og4)!f8 zP#@8-jgR)*`!{8@U<_L(G7@+H@rcM-^qL;!6e-#b=O^3#PyMT8y1&x@dxk`%qt~d8 zI8PlCGn23x(Sj=dDdc%Rmm2LO`d%EutNk;n3i9d*eW7>AMPBx^GlB^Pxhgb=WLr@- z-Vvr5%G}5d+rJRfgH>oJ`Q*d)*O*B{3m=%EL`V|X_ci<zs4XRs4PuEr92MDQ46Y$e zxn9UQI8%J2h`T&4vPKS-y^A2j`4>G>j3OLuSIqf>nVBPlf<a`!0(X`DQ8O3J)rma3 z|A&{K1Jav2z@K@f!javOGeB*~9T=1ev_3@qFNo19H^_yM{+QurGyt0c2~f-=x^6@I z_gs}8{!JhNJGeOszePB*nGMhot!4D-$YnMjyAi;`g#7==bPMg7k)q#H#fJI#0TsCh zriNijOm)dj9&-L5KSrGPLbzv6Je4ecOkjuq_XtuPN{WMT{rZ;CY#<oSB28xovAGoQ zSy`p)o}3d^A#Z|Jv?&v3AP&%HU7C4;F!!Tn3d_m(znzi78#@(s&-QyO2=@-_E~W{D zGv*43tA29+d-UH*I3$-~)*6LEE=;cbwJea3`%vEOsF-j%{EwTgNo4-n0*JSF((MjH z_6_$Fwm5dBr8c3UT0`ChQuRk^rOTZcor<vo?0p|(YnrbvljVaENg`c8;e{(lq?~$a z{aHlR(D1u{rk|VMNJA`xqz=K#FlIW&L$vU-;6^U04`@Wu#QYdfo}wMEgbY_gRig)x zpj6Oh7-(c+3Kvas8dO+_P|PK-bZ7+r`ooX~o4dKFL*Jc`^oWCiK@2(N0=7_{dA5A0 zI0cfw;9b3GHyD2~t^2A~-1U>!{R|EWQ2B&%S60-j_Ua^BVYIjb1|}n^Rrct9T^bLn ziN)r}V&tMf3D;Erg!11?t)^hTL!O9EkB=U{KWAuDSQl})l^?84NZ^q0q0YQS9y;1i z1eg~=%!)uo{OsWT9W3od0!q^;$jcQ0e4VY}wy)r_Hv@-YcNWXQ80}4KXyO`iS@R^? zqGVLQN2^lrzTSJfN8}97wv^PnhsISq>@4*E>LAciLCS=;Z2iy<wqbg&%s;;o?mQ)Z z`r|RFztrI2!WGgZx9Z$g@&>)zF+41@)N~0Y@bDTjB@Bd`|K0RJ{o0w{iM@2+msDiv z=!gd3kbdtnJbEiBp?_L-DJ8qVSjD94b5c&hTKt(WwfY`JXyET{PbTy_RAstO2A<!& z_OP`z*sBp27q2#-><B+!ZM+N=%Ym8{cp>iV7JPWCH6{`&T6H<Q_7Q$rL(Op`=65F6 z$2-h5ad-dNannxL#d`4v6Y!UfjSV_sCo3x>!+(3*h!<B`hMHWYW9mr=RLiidnl_a3 zX3nVeK3gGBcv&dmID?h6a4$W&Fgkyl`(7v!f`g*wKCf8lUb4wo)6kGtA>>XX|840y z?~Vh06`fI2m@IQi?P&1G+Jc`&#`OJ=?(NKH**R?}X~pJeFj|?F$?jRELMM3rvbRkC z31z9(MVMH~6EDmC`K#-5>fu&-!;fvE&F8E&jZXwy=E9wbwaaL)k43@(<*uR0%sMEn z9alI4k5^PzGxsDwUS1??YwN44yiWhS(}{SJ;ZC3Z_kf#8VZwnyx-E~*ckH%mCnBr$ zXPmtox1~B4vNf+s@YLKKTm->gmtR1LX<C9;(N<0fY0z*Oee?B5GP3_7CeP+GiR(#; z`PS>rM9}5?)u7$u5H_!U8zBL+KAdn1xX5n9?GP#tM?81d@`rUIs~(*GZh!7L7Qf56 zUtFAuObbTX4*@PNX3(RMR*mNe&WIoWioK7E8w)5e_hfjM(L<-sJ9{#KkHkrCi}Ul{ zU0qmb>pmg}%QG`*9MJIv6VVv1uVHjKB{eF0vpbi)<}jyWchyFYR%nf(FS~!~vTz>8 z17A0&v!Vu@1dt~?tML~qbWk^6@2&059+BNH1?cZDah*1rx2@b1rDI3~3<Mh`NnI}f zq#J414`;v6Z}!0Nc)uKHx`Z8Wf7NREPV;*07qYDxV;g5VRfUnAz~AR>wQKA3z`f+N zMb4ztqMkJg?(_t0Ec(2Z_ylSictkZ%5^Xn<t+8ZB1P8nS%@Cl?#`WCD!hZTU(AL@A zsU$dh#C%uuFIu^N14@VY4G%o_MUWgz2a>n_gV)u_k4l8#54@nR7x+tqzNxEF;xP)p z{_a6~(TTqttQS9|1#n?^_~SSJhRN_c9wFiN*Hmz{g^+nZsvKF^5>C$B*hNrgFKmI` zWq-}D&m+qOIJ={Oc(MIH#O|n#d#Yc;qO`xWIjyKTRXdFsegDPW{#<!N{0)qJW1iQk zFc=t@Q2m^G@`Gc0B|67xI!uW;Wr7}Km~DREvGPPeEWtd57tiTUhGGU${>#9?2>q?~ zt;@%|Y=X>bmT5MAMJY#+(opj3tQd5c;w~h8O=9Idb3Gp2=c?+^=&<|v@#B`tbP2hU zI1?_(6HT%%HUBU15R6{q=?~kZ=b5%XjB6g7uy&_Bov2YeDa=YjG_%52TU1x8@5k*| zLsT){Zn*)iXSES~_k$84F5q;1R{pNPVfMvURk)^CHp??h!wO-^jCp+I%VG{erYHsr zOO?#b3e~2=#=_$&_UD?FTC|-egN;)4!p^2@YHqxqx5Waa^O*?tf_wKfaz3<6<e^iW zuV8aHY(COraouv=8koii(XO5T_39s!`l9|xLx_9YY}G^i4t9eiE;3xGmo<cN6s@nd za@&1j`fg06?ji8d{LVXp8O&+egIp*$t`i4Q)n?-iGFny5uixx0+cCQ`g<IvES7HR7 zn*|7lXD?sxUw)Uh%Eb@x7`!7awOFTToW|fHMx*vLxy?!)F*vn7My}m%xb*6EHY*9J zt(vY<SrP$=ao{SxVvS>e#(F9QJpT57UBcDb1A~)zI+3G8504PygdsNgJSf_;wA2g$ z5$xafi>}|^{<3oMJY>dySnJyh1abTfdWer;3G93c@%i=8r`q<eDxsC__xn34jll`P zli+rOG)Be8=p=Z<mfpZDZ6g2vXz=8F_=+t@=84mMOx|JsWnDu;iBCgjE4`B1hhKKn zuf5m5t)=<_?icnGGMkkKsH@o<m3Z~~DvN5J8ja4FWj9vX>AtCjuTsnL?cjNS>EP{? zWCP@EaFI_E6*z*0SE)n5IZTB(dSjJ;#c2HG%M^>0^HLne(^r;Id}mXXhqVW}>C{lg z^KZ#LnBKRz%?Brp`_7)z=Ns*Vb`u+)Z3sw+ok7K7<dD~$>I;hAJS1bM_dH$QOkn6^ ziQD|GYyiw=)OSu@ded_qMYn~K5-{`-W>p^*%3-Ip{7x87Bg9#3ap2&j+Am=^Sf8K& zi{hlOL6{s5MnH%)TD;)L{0P2p6M3m&F#?55I(;?l>+L3(tL;Uv6Pg2hBRRoeUH>LL zSzKICu-w2WN%NA=R`og<{nUP*f64XUU8eFx!u>VRf&gWH0s`DD#mHqO6VYx4ZcZ;Q zeg%Xp{*~8t?I>!d^lN!qupJha@bVHgmE_;{ih)hCT8R6Gfq@<_Jo)Oh)dk&rN3=;W zsnPTZ!6A}u7<ai@#S`aPlO+16o(C&|J53km4XRF3cx$=J1&7>`-C~IP5%W7jdJPVr zne^JU7}7yQ8kJfsbB5~HdVg(x1hgHs{antN?xbb8atB{_zUKoZ-ZV;SlbSQr6-{-U z0A;h>sVVAoRwrY30#^CKZ`{_Hr?0(yZkgnP`$|oxeS;d6!+0N@quo1bYmNB^)Eg^0 zzPAn=u$FE#ww!*Gz~*Te?4FTotiAIXb4u9en3%dM=AzU7ZAnz|t3>R%aUz-<1Tz@) zxk`L5C=>U&k6fmQ5Lfp8u1D9O1n4Z5DzD*)H~1|itkd2x+MD3<`h?#JrGsiL^?ldm zeSEU13WBA+jjOI`QdJ{NtK^*c(o&u|PVE&;km2*F6D#X=PpAbciHP}j>ZGLyh1Duy z`AHq&qYPARKC`^`(+OdqabTd-ZKD`5C{m$GUJk2Wr;$_0;4+{EsqR{Jd6X9Xktue= zRsAX{7J4AxCjB?7*Q2JcZv8#V%$_>l!oLs0RTc+y*&+SOhj%UJJjBP<U<X`@$BgC> z@fHQRhZ?oTn?xyPD#qan^QaeH+r{18of(UU1<0#?+AAnQkv=PS$e}O))Tty|cGUOx z!(>=3Lcic9^K~ZH0UPlz!{;DkMN<~vOJu7eZFKP((Ucx@R2^x_mngsP+D@X<mP(?v zs@E<HphnAP+~~o#$ZscR3ShN@n)%6F9bq~(eIsx#2H17BW<CvlcWzZ3Q!rD;G;9{5 zv8_bbf)7(IUGpcig<viM0Yg;9cnNZb1Ig-L8Zsmgt>`)NSaO~n&%%S2gZcEAf5&K0 z(YA7=7Xuk=2{6l-={i8MM+OAgx8PRu?xC_N*Ww&81YYRSjE8Js{OEYR06JZrxU4Cz zJC@T|=y!<W$;|LOYYXe>S>MgbC^W#aMM@lfD?R;vNFpWBdxH0+ksdP>Yy4Jw<>VVb zPkS^6jUL{DNV)rd&&Sb5FYYm*H75^Gx2|#Js0jqdJ&6z(NM7oUY(}U;tL2N`k%83r z?e%lJyd}{b#qtuM7b|XO+0QjMEki1BiHSb{e#z@de@(oty_>AQENn@gISuUFYciwi zdvltapts4q-F)Uk4v>v-Qg%BYTwal*v*4?lIW0fL59ynL(|{p3q<{f#GJWr~<cGkZ z?KqX)@}eM^gcyVXBiOVg`&#93iUOS_2@G}VkK~A;%3mk@zr0$p>St%YB5x<+;yHCf zNThnK?uw2r+HZaL-Gyal1LF|W`NVIERu%S_)pI_<M|jV0KHpcrtxIj){*Vw>7(6*Q zr>f)21^t#a`JvVhwzB?k=m98Y7d@dn`m-c{1kaS6lHT%;Zej!r&XJNL@IK?bofo5X zL!d%?+!<^RY#3Uc7Pc=e^;&|GxFox(^D!JM<(s;EEI}~mn--nTQxwB<?5Aas?<@Sc zG{WU{@3>&T=Ba?B=6q{CUq2|nI+di<@M0EURIz^d!jOmU1x_HXvMt)&Wdaze3<6<- z-C8Tx^~<v}gc3x}qZ;;L2mWDeQ*0@;BNy5F;Qrrb6%GB_@MDH^FtL+cR!$jO`B;)Y zR~}pv>~jlwhdH<mEebB4>XyHrxe~D9{^2nKgxsOz^SZqDsA0mq1K)50DPaq83g4sl zGE4?Db*UxA1oe_%%($~0Y@(MbshY9>(037{qYu&9p86RtLLPx%ng3E~@`_ZaX0Maj zrKfDIe64m$Uiyjwt?_?U!mhl(ke&1q^yd$e+QAN0?C?JuK0gdf?oCmB4;CGYOIwap zh=l-E`$Cb+40*XuAhHP)+|pQ-sGLD;Xus%LLFg@v!uOP2k*>4iMhM`Q0!&TgFILRe zm<%fZIr_ZwblB7rq0=O}$APWo&V-^>7|z%SBB(dX9fd8iUgLq#x$+~)9Z}u`qJf80 zW_ZHQrJ6S1UeUa#AUp$lv**k^_?2)A-07v(-?2;QzOA#_fnM4C%Kq4WF6=kT+6os< z%8#(xbm`yJdCqJ%IO$E1tm;15x`8n%^x&^cOq}f0BU#pDy$QY8xqWB1W6C<W$R%QK zj#$2!+ZwS*=Xp?<9q|7ALKc5Y#cqa>?tv1OQTc+y0a+xV*yBrtie(!5*C-qU4+7Jv z>`^o|ret8xzb&PM8}0$vHJg<o6B>9@yzX4QY_<L5?%kWiBNlK4Y}{Nl^<sKI;_`a# z0ooqcFMW%$+%W!uvr7UAG&XuXu1NHTpNlq2e}2Z0n@{$h%ri1xUz{EJI-M>IbLo8G z5KqCa?EuwgWkD#)*aueijn+cRS^FuY>o6V)b?7>HmI&Nid26-7am4B+PqwGppC1tW ze!v@-XDQ=X{{h;T|C~_>UZgzDUnC7xza~f=u!=xFJpXk)LAcge6td;t^Of{x-#|G2 z=(gUaYr1k3M(+w&8N<DS=l0W8kk32A>oA!Y+Wo~kW}b=3RpuoairZR<=$XtzR!Uy_ zH_m+k*_&2}EveU;J+B9tPZ(1d*v6vy)WZJ>kAvyw`qv+RY#vKy1L?0FuJcF(VxrT` z;VvyrQZ>hK8mo7P<tjS~Tv%mM0>?S@Rvb|Brq-3PHHz}BKmq8myrOx(I2l6PA_|o) ztFsFNM8z;URZz+r*8+4sC)900^@)r}g0%Cj#i?XLV-~2S;zddql*Ypy)@WmGijV|X z7`PL}%JphxWrZ^-DH?y6lVfyrap4~~&1PX$OeoO;CHXgbl8`)kbYycUlRmm6M5psf z&-Lj`a<AKA7Hs66k%RKW7hr>em*!qsWhKVR>8T&2o<PjMp>u6N`8a)p%}Jg7Twz<J za_<BwGJI7)LP@wxwj_hRrhR@V89!<67!v*p#d%U_>!iWHC_F|rwZpgY@%zOI$ZKE{ zQK*4^E&rM$<Td8JZtFb>Z}dK=_0L_ZP(*qT*DJ$2HfnC@(>EONF%E}VZ@vW>@A0E} z^waTO?HGuHq#i>yA&i;0->{}Tu>GNTX3T5=5Wr>teEPX;oOQDE1ppnP`%V$<u-}r1 zKgAeNZCC1s;FZRX0V#<MmmCWU3dUw<1-lzX^Im2s{3G@w|G;zoS=#ZoC)XGd3!pD2 z*yk-=jz!^M7fnKK#AVb@8AmI*2H`MQFwZ>fnr_l-G3|9MBoSDKcU`BYr$d;_InpWC z(*Uw(Bw_(aq?s-<&%NHO#-=wu-~l2xzOOes02LehV)yqkke5wG30G?9Q>ki~RiQ*X z&T~h8jr%^z(;w5nxm~sp60r5t-i=tr21<K79R>Q?Mmr+lCgM{zP|$j%@9aK%jsDGB zW1h|6Y39(du=!4BGw9{QXXqU~-1(ecZT!S;$mHoNfLM~eJD!0_VbG$*MFl@*I>}j} z^3of<jg1A_nhgCZ{)}9Lbm@nL9G{<l=lssyNp#u4f4buG2Ddn}!RCe;9kCCUc|Eh5 z6W;2UI4WIM93%>+qXfaZZPpjaT6zn96J{V{z<ueTjks-u_`nu<Yx+Ro5buU*80QLA z@?Wbx%e|<m8S0f5w&O+?E_)BPYwL6B?0w4+$q4r?2*wL)^|t5HeXC6t)$_v8_xwxK zd>uY&WM_et_K|4Y50Nasjy&-zs+VxOP8x-9X0pAT!*a&jK#FjRuBv1Fw>0RI#v*iT zLzLvsGgT0-&(oq0W+Vq(<$KJR3^9qPDIH{hQz56@OWCDk`uNjEY7r^2xRqi7kRAs% zh9I0}4_99d!@R0=>$l8c;m9}WlYXa^5xV!8(K-I(h6alme~TE;9{XuiorqIjJ&@3m zHOau~C=eC3_v#8M;|q#6p8z{rmDjjawf8#T(b-vG)utz7+h6pm<d+SXSr1dIDy}lk z-T+Ktw5s+CJ^%pV>-B=!<#uQtyxQTzZ?)Qp4!D6$=qdP(Id%7D%JQok!WTV-jmnP? z)C1r-ofrx#Dk{cVlkMF_f-<lEpm_sc+vPjIU8nXzqyDVs>CTT1dV<PFrv{m*#qy?| zK-0o6gQ&0=OTX_@ZgRrZEPPv6hit(eNdBW6PefxDg9wA4);Ap`QOM<#s-1DtN?uX1 zXPQdd=;lDdt{eVP{Bk(K?qrVs=;S2u;q`!j`)GQXJyYk_Z2WwsUFTW<Ot_j1gOryV zY02Bd!pisCXc7=QUHa(KbM{Ocr8IiHzl(U>KXg2uTTHu)z2kPjYWR{diIq@xl_oxx zO;PFB>(#!n3WT#X^(2X-1+5QQvVRU9w%zYpMvUw>{PlM?dxJu_$#!$ww)J7-1=h0T z{@n)Swc|Y*^KsKYQ(EfW7l%=Ae@13SHx{V=^)F`Jf!L|R^Fvj492M!bc##LKK@r`^ z1Jj#>0Nrck=U=5QRp$7i^8~u{rb(RR-e^LGR`EuVmwOhw)Ij9O4AkPqvgotcQFr#9 zxF9cKn^Yj(Y~wk4rwTUC=(uu?I^4+jKCONlPRG)Lo&~p97^$d5ec^+I`UA@J@oq+a zd#<zPIv!4v4vt1h=}fbsxNNk$Fe;rq0o|Y;)_|2z&k>qBiV7_G_%}%g1am%)(e{ug z#k-ERAbn&iZ#|n>!wD2nzwgzqp>%@@KKaNc==~_(2^%?!OWQyoqct$BepV&59EB!s z^lYQnDnB^U^sla6jJ`#=oc$D{B$^Y8`JsB-0Q;i(=I|={{nU0#yz`;;svGl-#dI$p zVUWFdF7BmwAs~a^nS{Bkb5jsQW_w+eX9!<Fk#K3_rEoc#HaUsYes78G{U!X{=>B0+ zQ5H7sWhPL79mcw%qT207s`<4xwL}AOBbQ1V=V{+oHA{y7P~nJFrW>l0onj$=!~|L| zB$LdSXX{GwgmC>q<;u0}A6N@P`#*ACFI-QfZ2e9*Z-YeuD42y>fxio6FeJTQf_gY- z*{t}{rQ^7f)%K8Tybcu~$ArCvsuiHaaB)6yQVdKGkzBs7t94#A6K!788fJWmy(GFL zWmrEPS=RR&Ah8I9)S16wnL6#$zZSXzpY3n_>cg4G#S{dTpyqQ#P`VTuGBIe{`I2vx zIEAl+qIQ2G-uXYbl126$HA}*);X3aP2DUxkx=%j|SU^9Lb#B0{Gf%X&n*b8?yl1Uk zpNZn`yE5XLC6gkL;Zl15`T&lzU7yEIatxuX?U)fIw9ob}DWoxb(ngY%@BnAmhw@#e zfH@gWbuAQ<nni!kcMa+>Vt27kp+$>wIU(O77z8=%0LQ8M41MX^Vy^Z!BGojDUuHTF z^>#&qi(L=hn51$nX2Twp^kPbkgWwd_6cE0%Y+--%aS<OkGx2Blx%X&^;{l#d83dE{ zHn)J=O)>vqGYS08(*;{^&55MfE5u%8{AVwE2Z+1b_+uy^V?gXm+QIuM@H#lN*t;Jk z_j=xhVI9JXCWL66>Mqmc-FYMS2#!YeGYU{M+RkI^!MTAJclbuBWFvWoTCCjjz59&! z^LdX&PfbbnSAICrkLixzJWHwlyaz}WFwmm^D1(}xK04^X*7~u5h^^7HKC#6|Ryja{ zq@;`DF|SKkT72dd1Vm&5$id8a@@HlNuaERv-l~p{>+i@X+DTA(&X6SjyME*e|I8U( z$92J^oD=Hs!hr&jf6k|*fVKOyQ+@h*wte}S&FOp#sxK*II9z?6yc?Y_N0~r%H0ST2 zq>Yspp<7;Rji#6jd}_vGkX0%#bQ79w$R$gsXsJQUg`V`KFXP7~1kg$<MuHEae>`gf z47Ulc>G73F4tHI?Lo^p#=XEdlLm--*Jm2pUF}D7;I8U0)CKT>8sZ2JxMTIKCnK7bO znq!f28(Am81m%p6!9ON`_}Okguujz;upf<PVn(fRcXPfUG^#AQ13whL`7LsFpR$xA zO7Z3vZcCrbsX>ePH2kp^H|3*N0nI3*i<5WPrc%cJE?F`*cxt+(JYDrQmBR+A*Tn)! z=p&^_A3Bu;k$D=d2}~IQ^#J7M&D*v$9>JZ%&v)x~%|MuBSlnvh6u(O$Y#5-$Yxn@! zkYxT5zm)Q=-^wqqJZzZW!iEoA=)?gqXAgg}Z*JM8c=FtQ+ZX<|D)jWUy7g!0>z3)6 z5WEiNzR}>csj21Ip_5fmm;~3Qn>q>#{N?jlBAJ#hIkFnuS;q?*Cl2uA4eI!>8zXm5 zg&T&aCql0nS?McYAsyk5>#(auh+FS0ZeQS`S9Le>AEN><?7Sry3?tdcj6OkF$_ChQ zc}E<NUoSxQi8OiVd=5@dCMu%`Otc;&qZncDTtW;IArv~r3^+*b-B*RFYtN4CwW{-# zqj^6`XY1|dGUUMhmnd%I6UPA_C(;k?It_D_k|gWvIraum?4XXdDTPZf!ZG|<JQeb% z74~UV1HETHt<)b;0t8;2Kx@DSi7=ck-z$}ocN?zIj6F90Zb%o8bdvX~`WA2K{-08h z$MhbBC97;pdZQ?@r?ixm*zS)mm&>T@>w2-{W<it-aFELJHl^Ao3?|tU-22tCPta*) zD~7)NG0o#&6WHoKS#IQokGH?e3Y!fdHK&GdJQc=JVEIl~MUsic=-ytupJKU~Y3v@r z*P<3b?ye>Mi$MCip9bCb0ZPM~#PNZHxjtIz+Ujj@jfdZ5mW0+j;kAQiURUB*2Bs=} zSfohN`rdQbKE?4GqO`Q@`&h`==~)fZg=LWI99nH4MWLhH9rqKCxQdoWL#9d5>la<O ziL@!`O5EXeQ~l`lfvIJu;;|N5%33U&UdhN|CVP}7EWQik)bBg<!f@Vq{!8ZG`O`j^ zQ5c4MQuy^;K17rF$L%=!dgi<*p|3)8BhpkJTebx4x@i>Hz5Vn#&LqD&4CY=#I}>LG z3q?b`Xbr!?kzDqZ1^>i(u#>vNf?tOpbu5pD=)%-m$MEYAe<-8@jOI?G@1398MPq>o zyYdJKXzj;0;$h<i;0jId9{$R-!9&?L))VbyQ4%}Hzd=x|{07*}>wNL|A|FiLy&#Sj zLa@iq6;I(5YnRyJrSn(^J_S=66w9tJwJ0*E^I+PRDR=bzsxT5=*jTA0ZH&xDvJ#Fb zOYi}839Y|V*PEX(_9&te;$o25rIFih@+Gc1?IeZ)I5qGQ$2bFMm<Hqk6zQm2*VIu? z!^u$<D#;2fWfjC$^+M+1I70k$-`Wk6EbFBQP|-7FH$pRtJxqG)EG%{CqI3xkh~12~ zm+h%Pla){vEp3LYkjQ%lJm+h~|FP?mMtq`@ZsRrNE&E&e(8dS|77K8wlul7EReIZ} zBWI}=;1nF$eLKPJsPkUC`{Z;T)TMMM$f7(wBQ7Ig|H#``WnI?^c2X$Yc8S?!R)}dv z7q<PdLObFO4;%ra1Bj7$xXg?{-Phm&;q~-6mS651Z)AE-`kofK@_{pAwQ)a<-KW09 z)Ek4g_bshKbhZ*7-S}%O`jd6}?iA|;A?XfDW9ntN#j#f~v%cz#NNtd)rpml(gxRW? z9Qjjj+_geYJB!cP*E>r3)ZSe)cAfb6D(b)4D-+1YkQB+g@w$SYVCkI7qk!T5Pk+?` zz>pwCommj8Y=z*Ev}y#IEGL2K#8X3-WU0z0TTcE!50K^CH$8jOHI$E<FdO>`$oq+< zWG2Q>3pbi3h!Lh9DMED<i8}h8R;flVuYxdf#htRCQHbk=mK3pErrzlX6)yk48q8*$ z47|8h5Sb=UNuwu9LTmUj2Q&G04^4n+B&&J>u&wX?(ZaBC_guJxLnP&Zu?-HMZ>=aO zeT7HUN?h!IP%jypiY4~O^c9FlyG?%~LPY>wCkT2Lx@-mW{wr?h2ef8o26w)TIx0`_ zT9hL`4)|OKUM!?009W&w((4>%L~`$xvY+rg&vvt~l1Om^PfdJGd1xq~`1BT>O!Hy; z?HPiKWX)eB@PG-=-yFt@HWi#zl6wRG5j@|Ii0D!9`BI@-OP8B}>2PILmWr#s={vS= z#3%=mEBr>jT5Wm@;bc=Sh=9qwP+=|`7#rfP!;&hrdf7o^DNTd7WRS0bvyn|VBiHyh z*Es@tGth~zlDX7Gq7$4#c=l`AJN<mc`Kn9zeI~!$YVzbKZcV({F?t&79HzN(q>vqU zKR>uYT#mNMMB9{e{i>BHfidFY1F3(59aZ0<Tc(jak$>nB*7fCA*n;z|{*W9Q6!zNz zwWH9q7HdYSg||I=DGr^HWW4L0M6&JRngT+cq7A%?^L$Rbzxkb6GqqKse-v&&FO(|w zGw5$NM8k0X#6W1FXp5rLjrrjpr1TDTZwis@KlJv4C~D9+(Ym*K3ac3#P4FG;QMe@8 z8}ZfUz7rx`SwT)K7UMu+shsOKym`m*yJ1A*akI8nnyC9O=Yi+KI3A4_ibD~W1V!=% zGqn3GM;&t|vi^sX_F22lh}p2Un8S*ZZuI19_vj+rTN%y9YTN;gNM@=?csMWiFlO`~ zPPgem5RBB9iV^z0M2YUbK>Gd`H^3Rv1jVjk0W@7u+q_e}{RWN=D?UF<)P3x{LP<r9 z4Er@12=63rsBmhb2>acix%K8M3NZos%K7eqkJb<-PmSd@dv(yziXoNgQn<4edqOW! zRsAR>6s1GKTrjwT1j1cX&h23fB9&Q+8FRr#=&I)+tseE${`o?RdE;L)2U7zRsU$Az zrv)t~k(q$mcQPVU9<UqByOO?!2U}yTQ-l}#WewcB8!JYx!NZBgD8!McHY`BMft)g& zVt^Lia@9+R6Q06gx1F}uh6{v0aV-y+p!}y-0Rg4D27Sx5KLTH~YG4Yn?z)zw1Ycv> z0Ev&+g1A?<ouFW3s_B>EY-cfX5+L9PL$X$WJM{K3*2>?Mi8|#%Gr=S2dG68$*ZA{x z`tLe^Tp76+iq(#vO|N5wD8_yJ2z%BDc^Yg2S!-Qzi8C;p*O4DZeo0CcOwU5t4Cus% zvUTKaoAgA?l&r5>5Qc}7`fww`3&_ofFc_2VduXtRAeY4CKRs=4VQCW_!6HETPu|1! zKJ|h<>jDX9z5~#svC6ufN8gEe3b1Va`&7~5Yc7a1&eiv*r{<QBDy%D1!P<X}Kh)RP zi?~2ko1E1NtdO%d#wGt|@XYPpW6tF0`eqoN(V9rn{dHa_a3awPiO!IK)*MwXXEHFy zGQoRETSv#x<dpBpYd?JdKbGeyB#kW?I36vZCu|5jAhQ3$ic*G|(lHUISey${)O--O z`p4`Zh>Qbf5f8b4QU|DvPPr)`OCNy+7;*D;PlnhMfY0lAjAQCgPvk~-pQVGHKp!n$ ztgY>S)6o1dmsY!GrEL??x~yM;RY?}x;Ki{ZuwKtl)p6l2nnJeg?BRtKov#GKGclKi z_DF0b8u?otecwS;AAIZ!bUZam$+^kkeYtyC4-5t3MJx5}vBD0s*9iT5{zGT@WPJTM z56jvgOJ1=Rn0a-^A$c}XqziI4*6|{s8VQILPUK8GZ7bu?lG5SftM2SX@-i821m%$* z60(er7^cW<rY<dfh2V8wSVKyc#$g>kHn{*_!M4qyRRa_s&GJj<txjD_?zhMorLN>o zXH?HCmkmS(1yYJXH-`^BIaJbn+(E29=Nip#QB^@-IP83CNFaI`*6tEDu-$r@KJ@&~ zi|}hn`Q@Fhr(XFjsQFpqYbi$dOONLEzNRk9qMnY|GM~-MG)eW}Gbg)g`2Ar!SN$f> zQ|`wjX(1?7eEhbMwGP@1+(AqxXJVi>^y(4KMdKeBq0<EVSpNlt*geK5k#h3uECy;a zZKx_kFP6-E5#x|#H+KA5)G7Xho*X~ZORBMlo^OD1o+pv=>-h=$KLB&6(o_=L@W|}? z57+&>)0j6|YAm-gtx`1dah2lI(Xu#xOMg6K9#<KMQ&-;CP<6JwF5M8v;C6R+*;#k* z!%p(Pn&?lqKgRFDF1DY1?2I;Yy?Cm)&Y1h`c?9mHiT>eF4K(^3)A0taRa{-7bNg0> zH21N0P5%qI22&To_L3u-VA47Jf9J^`WADeZ@MA9OWWZt2#~?Vg;c<FVVtw!aZ}HW? zOY7u3ZPu#waWd7wV7n0IA?5932uVv62m?`L(BE`BJ_ue_kHN->JLHR#yhk^6e69xH z7(B{~Ft<SVrVBx(kTM7lguoMeKJG6<f!%s?EeG}rfsbnwCysE^`s46<?J9Ut#=&h+ z?_mUNz*6E>++hUPtk9)B6(6Q4blddUpr3s3wiikv@H-U$Qu+OA30G;@-K4z|$2!8N zRL}<ZJE_g!vtg^@pA^znz-eXU>NN;<E)u|H0aQ?~r*H@o<g*3{E2ySd*g1I~6aABi z?cR#%;xmpV79{ynA5LvkUGvAYQ1d*71j<^g^}#B*EdAYtNEBp^?M8Vz>6)nGL=uSN ze?bZs<JSOcR=kvh(RX{sHmMY_eChmzsv8Ix3tS${1W8n55Qg&!-ajVzz1lt%Y9f}C zg_A!BKZcGWj_NiTrxvg3m}*mhFV;5tL?`)u&YAkRmN2znNHlSR+xBO#cWL3oz+!Df z{tdUq_V_u9>DKpdY4|@MuU5t9H5(6`)IBh|Omwo6UScIq++5+TZSWeey@-Tm1JUE) zB88Z^9lpiK_vQ}#6DjWA`dW%q>WCclyr$p*%dv4#sVP*?<KN^DyX*W@$ek}%c5!P- zbDZyxa(&JH89t;fUp2pwx#Csgb=s3Ls8wP2K_dsklzs+r#|r#rFMth0h)03Lp`_NL zjStR;A4XnK!5LI-Z+E{)blY_<KjhzV4=Bey*adS9pYuTu&8UQ4d+nvi7WO9VnP%>r z7N&rvKoP2B`X^Ow^y)J<qr$!xs^RAZ;n%92I<?phlk0@WL&bREq^QH(6QvuG2CNtb zRHKJdQYtRlyWEr-OaR#W=DKPg8w289f$>4(h*~ii9au0m`b<Oc&TssEn@%)AD9ETd zbkqXHloX<3K}d-#@YI+{w}(Bu^rATusPjYuFNJ1xoA@|UtKNI5J5zh+KgWE|!is3H zMH32ON_8Q%riR+AQXy|}ORfwGgJrSvw%hLK-#dX_KRfG8ThvAHE40bGC2=YFPV;6$ z`%rvLbVvzMh^!~SFlwqN4(f`rk>bM}h#rb_dj~?qs752n<$<`m7GE~?(TV;O_V)N~ z5T*=$sd({B^+j1UQ4$D~qBZ!sOx>76Zzn#CiJC}A4MPS7Z{EbHZ4<|q>q(m7pBRza z;EWK)mrWw?+!pyfBI2JAyh-7>W|OOh3<pAybmCBBc{R7lNt|A7=grK8!O1@tW7lN} z-Ap4eohn|*r-Q2Jo=iTFdA=)n>sjY80!bENLs>t@cbhpxADI_<HpU9%@`ZN%EavRc z`{YzV!JTsb`^{FSXHU6GV3mHcgdjCkHHsFb9qYc83tI6MiEP~)>ydb`)!zWHQy#Z{ z5vb@p%}`k+54s<6J&%-Q&yVBDr9TaN16@}&2#q%)AnZm43T+i03^bRDFhhlP{hYz} zY422c)<Raz9g20Bk#0arq(p=Ndp!9TvF5Vgpeg4Nc)Z_1_Nrcpnq*%|jWseUgP0Uz zigIcUQ~Cq+{a09xG)~ITf)9A3Z70{G`A;}E%1+7{x8c<1^(k2P@z4x*^;Wk;;<*8X zhC=V;6Z3np!PH2j{r6q<O9(uuUrwLn<AVpi-wZk_!)UT#7YLJaZ&xzrG>Ky<_~pC6 zg#t5T8Cpdsv<a=~adOzlXA}7r$3d%&<6Iw4u2?feh<BZghC(@~;*5WHT_oY%OcW$M zNRHOoVdn>^!_=??%#q|TJ~?JrdKXw@TSk6(E1=Ic5J@~FvGw54b@Wd4{p}D~M@&!u zvURyetzIf!FsZI)g8Le#T!#g+6T0=b-JB5g$Z0K-D^*3J))UmrGIk1B3Z@lT51o=& zR*(-4KUy~aX~fvEZs@F5$SZ)TeprnW5=|PASSE1jk;s$ARkQI(0{&BsnaGeZ!PIx_ zAZ0u(j0Rqm|DJX`%#avCD>p_sTY4iJA4;pO{&4GQ;bkud#b6VPo(5BVVmm^Gp5kB1 z7tHC<x>QAQP&k-|H40Y}U=|7ApOAc*6=)=}H+|Au8YrIOo_ti#=4(D<Uo{mO>9*O# zk7+VH+_(r~05a-$VO=gq#AlkVz9|ksdpv`#-8${nNaPK5=rXt%zJNLuqgrAeeS9-T z<X|C?DizYfV(ZXWWI0Q1*y)Wiav=Ew%EptM)!<mf1nts1^KnR|hz6%%D@dZb+qKO) zo0LT~akk>gJf}85NSXW4A^mH7a4=FC8T$I~kOW|6nYTQkX9Rt8&^hB+cNRlXR8-u4 zcyosLd_)O1UZo2yb$VpVtMW8h*71fNbw)vGsBkB)AMe^(_DNx{y_4F{&jeCTF_7rK z^WT7^`5UtTa-<c7ZNA!3s$Ka&nRWKKx+`yE=cDg1kCVw}hB>gAqjA3U@S$w)NLqK) zPb0pTG~{<O$m!@cNPux(E4Vy<{h8f(gacuAb`)#tivMSKK{kadB+5C;#VO?lh_}(# z$Pu`<^a^A7!s(XaE8bF=FX@xIjia81LKBnnh5iAkUeM3H6Hgj22fu2o7Hlpn;`W}i zhAfB2dG+p04amw3nl$)uxp{bckCp)SoIKr-qA;ZX`DKpoC^^(k<?ub9w8=^%YLA?4 z?%U|*&kxd-^J#D=a<(-CaI0TZW7AI(MsdL^f5A)2$!GjECF+Zn1cUEFI$JvrcYjjj z5bsKGndigyZ`}T1hNX(lQM0U?0&$Ny&hx6BLZ55+a{1<XsoBcCRq}fX5E#mB8lP7@ z8}2-FSH4uqaUGJ~r}sF%>h?Omr^3{vt<a-sz!VN8SHL_65d1ovu`Mp4xBYUwu_46k zy+h5UJ;%(4?%A8&F_vxbzV)ELid6NviUqq0bVj`@o50=icawjQ@~_NRc|WO+`{F3k z_DR(6oRgcm8=Tvy^r{g=W481ACIf%t#~InX8YA(sk{R}VU6SjePk%69^VhjVyPyxS z!^9Jg7nb8)Ji@e<^Xlx2-_I$lDi;k_pfqhuL-MCS2O#sie?rL|E53X#Wl#Z2st=km z4jzPX;|A`3kRrD-l~%HSA#G5y=4dDi69}6BY}0OKjos9~OKe`su}&aqGZV<dN#U3% z|2G#1%}A*2`>i$1?Q}3u?~%ItqMQrT#0Kr;<Wz07teOMoI%`n79Ax`$p0Qy;K=K#< zhq2DPiOK9W6E*r-Aiqw0)#(7<$;xa}oR16)@~j)9TC>Jzys(dixolm=uA^(XXGwHu zsR4sl0i`~Jb&9Iw=t5_DBhB=r#6DX(`wRh1ZfI)7W+5oADhplSDO#<Q6Dh5w3hY6^ zAYPs_f?j4zTMk_9E}RaLE#uL8@~8DsVKYE;P9ELQU#_WSp?lBG*nsp0_J?igFLnG( z_S6S<OhFWW+p)kZGT~`dNK+OxM|TG-Vu7XisT{wdp`o(PYKI9AqzPa<^-$k8XB&H| z>D`d)>p!Vxq)?QP$NfdZ>J$xBfWXCaD;X^<-S!h<lV}64-BmSOTeHh1G=TI&;%fCq z0%fOpmIt<!lr;Fdw1ja0WyNPJ)=C=Ao0Q*MtiG4!&-;_ARsMJ1(Kgrf7Lvlkc6ygd zq4!(>p5i~bis<FA5HH1BX?NI00;JW6HMynxnG(`Qmd-hEz1pG1(pu{=DO8LA1_xaE zHxsy?$x&i{`|NuEx%?E*3jblgX1Bdo7uPX2uXER00>mSDA`5k1+%qJio1cm(NgS_* z9#4&|QaE31ZM6CWns$E7*{859I&qk=LY(Wo3X88kkItCrTM8*lOO+?u*yq~ZwSksB z)ByrfpQL=&8kD}4+;zWW<Jv#0HWMJ>oCdvN+&K*jp@WFyy(rHXmNEgHga8~_A_tj6 zCeqTgvC8$KA%MKnehxWbRXe0OJDgBt_;-iYuHRM{?qTgGw`hZf?T-Mp+@I$sXR$TG z5D@7_$<E1%#_xQFckm)PZ;Cu_-1YOd4_T#2vi3%be7IXvQq!Cc(w3l+f(&WNGE$Fa za-@f7j1yqXHiW3zD#^op;{#c;MD>*JGjSHFQ3G~7lUcr^A!3sFd3KN&tz1G{@&*P= zW4@>vWG5^ZX*n8c#V$dV;4;6fy<Qa2LmP)e8>?*mmCdC?sBL&db=!)iaj2Pg4R5ez zga+@UMsG?G^z=yVZXfNZD|gwIX{4kEZ-iJ*RyH<hM;SvKfkAvo9OjZGbVnHeeJUlo zh4t>A8?P0f(r41jS?00A^M?nmkk;jY74sg?Hp)vTTCT=SRT-`A?iQQY(U^)BjK;mU zZZw5+7u<!bM}-7Sn;7_2qY_F^UDeg)p&#IAq4NYingH%dOd@GWI-p+8gEIhvg;&bO z%`}#Q;UYK*^!wSI>@?R|8XJ}kck)>->J-XfzCzkMsbwlEn5XFY4A0isdLgZGA`K!m z<Je=~n;ik{Q&jPPbQNRcx{Zdyl?1?$J{Xy~($X^J*4Qp1=~lBlSzd~h2<zpgDRO+^ zS~PX;M@U_JSdrggXw>~1@t-k&>Tj}NEG;cd(w1&f_c2L2r@tB9l_si!udjdZJC%;k ziz+E;esP1yLBdXF(L4gRn~axLQ&Q3b8yC1sjJfA{T%Q)^!(8+|&IJChsl@f&^l`n~ zMG0F4c#CG}NPKl=u1hVyb*U)Kl8z<`;M!@!MCo@pKlxHC$PZ5eaeyCMY{<8JJ>=J6 z8>5v&D#(;quLSU*dw=$xe@|c0H!#4A3f{NM9r^O@O9b^?9QAcE0tW?+Ws)9aaiPsT z4iyQnMem_RDmll8n9!0<_3`=lo{<R2E_vA$dWE^hKeUENd;A%W<QgJ$+K7$B3|-yb zro`U(Q#?n{3ZFj^*T-5dutVikPxUA@(M)z#Y9|Cp8XH1*0ahGqLH1*9SjGvwV&Cmj zC@-pE++*75g^H+~L-G+r^#I;W7YSV&u~f2>O+p&O6I@Ybtp*Y*8VL2R*Wk8*bIW&p zNOaheGBIq1(3q&H;O&?`V!%3>M9Z>+Q(f(7KPsV#CEI9sg%Q1*z@C^qqU18rM3FQ; zL|a2)#jWZ7^B*$IyBFbKPfZ)06DqYzmG^fq${zx(OWou-aU?47JLzQhoDd~C(^}`v zoM`;6Ho0ZDAa&=byuFDQ7n9#V${)QgDVn{hX^09VREDu>y_hGza2x!Cxua_W|79KM zbJLv99`AdMZb_*_>tC4ksr29gBJDww;!BA_5t&^=Sal3efpHqpwC~J7GGJNV5{$Gv z^7>U`!sV5AfBH1T7qTyR3iyJllUmr8dp$K8!AD0VpFchKetO3t+G%ja#Z}^LFQfI* zUr`sI*-nX1(UfR#Tet=vnCX-*MOLqJ>}aCV4Oyd!x2TMS7M(Q*Yib9hvbjeA+7U_l zxMpzSJioafG&?fX^Pkk;_w-_bdAeh_ehMzm_iE3(OK!<e<;3m!jX!d)XXgj1ujknc zxq&1~$OP+zW}i9saS<Vqts*k@3ezKVZRR<q`{j$*qVRF<8m%ei_iZv_XA`4uyF>O6 zx}iJ8V~Ovp&dT@Sd>`i_N@LarrBvFI&PW)==qY>x+~wLo9Y=i(XSx<23JYSbN@Mg1 z+pUP`IrptsrDKZv=+Cqz_RALa=;V#|!bzo1^LwQ_<eh5fhaJy%v+_tU`jRFPR=p&f zcK68jK)GyP>OzGc;}V|qJ;*8hxyL+=70t3lA*ugRvWa)>URTP2q<O@#gnTN+y!RS6 zJG<(1)lnw<`OvLmdSR3GV$Oq-x*JCvl#Y)XCC_|4?s}!mn~_&}lzb32fuMEDOksAi zzS6VKrt}S0X2f}0&xr|tc5|^=wdiE4@~57yss90vL2<s2pH}AM#hwTpAEttZolj(l zXVVR>XQ&rux@eF>7y$-@!C=ILNd<|6C~~3p(}lN{;ziXl`ki*Z7%Bq;?>09zRVW&m z3QH|2LV)?=l5<GPFb{POj%OH>9it*M0RC*Xz`3gPDfubmZ(2unEQV5%_3=C2a{uO# zHgrqpj&)PztNLIaGpc;reuh5O)%Ljn6~xe2jnw|qIb#_ZKhOzg>eZXHqsmt&JQ-5H ze{Yo16iInmy262WuJm}IB&+mWwISW7&QYDWY6piLEAJyxZy@`ril*|y>p;)fzc)(A zl&-5o?g4pTq#e{bjEzsw<}8(MC-r)rq7WH|LOo1T>{MsWl?>XgDke@6z)%iJhjT#S z;=h9_ghs&7B8yubm`Wj5SH=se1SiLmC&FD#UeMb#+8RS=V7|qK^Tyj5gXvD;fIQ(Q z!M4uvf((p#MqGk5lYD6>V=|dhl7-t<P*#Le!4#<&7##LhE_$r`Q{)Ic$2@W5r}d#7 zv+7{=I++wF1xgI51xN4CXj+<ARx6X1;n%;^*_#wJJ-w~XX`N`@6==O|6X8@|YQL&8 z?dMfKxYCRw)#^G1h#T?uGnwINC=$J1raG$pRC@hbRR(QKlV6`{&_J)NDu-Shng-9C zmW79v!%&pNQP4c~@3g))G8U<TnQp+aH1uR?1z?^Q$u1=P67rb01LIQwqX`qRy3+D+ z$3|S3PMC1C)dO@l1B^^T;=sJ81dBO70*PaNmjfro7$NDlZV+v~ssl-5@|~39&d<Hh zV9@t;H7b{msAI#*(K~XJ>So%n6BRwA1@?kSbX2E^WI#pSS9$7co2o1AtZ7F{>lIk% z&v62sJ0()8t|r2vlNvoO)>q|6(@?TvYHM}gI(agYJRSOZ>DN`AD?OGTL&=V*jZB12 z&qtlN=^oGtFO9cFf9uy(-2?Qujjx!_L%)`KeQ3Lv+op2eM-vgH=W63kh|tBjO)QfB zrZ<H8O)=AlKPgNXTo_3qIB0z<z=45cDuGznbmZX0;{_O-PPXlEnV|N@3_Nord3mj2 zO-?FQY2>}8qYn0qc0M$aY+JWAID&xXU~Cdn2*m1^mQP|Z<{q~9cD2Vxbsna4lnm%f z8UCTuy-p@5*^!D1I_3lT(Z&(Vv7eGNl@BEb)bvT~qo9rffIx>#RT!OE(6oBqrhPOo z+CDl#RG{Ul-;^NGHln0ZCtKKdx>}^Cr$mOfr%JE4linsa;;Ek}ZF~JbqSwiGuPVZ( z@}b*w(x=zSM7$MQ!!}ZFTS>0U{)_5f^vZ(@;)ciSL|z?lY8xA@1k6OH^u|>Mcqd;p zUYOaw3>XXsgHavA-o>Wh3i=4%v|Uec>nqfE1**^l>w<Zd*N-gw5>aB~IYgts099Lp z9iCE{+|sedavo057fe?vC^4(>XDa2Vo5d(8@y;2HBu+P@WwRM5pMIId=1NP0sqMPS z)bISUyt1bhD3Yq52hE2PLrUgoTTlX{`|QzkqQ}rSq~tEx#>8LFRm8fiT45bsm*%O` zWhzLV;u#8*5YzM2ua8p7XkC@rSU=voMv9zyg8*Ct3|mJh&+0gRu%X&>#srLJ5S3~9 z0#zQb*4r~Z?6bkCQ~hw{*m2mraf2uy8#gO&N>|7ka_u|@gTXk%U{YWZCh}z>VPWri zN-lJDM2Fh1I&hO&vaG0NB?+VIRfRv2t2#yOYn)Q2uE>Tf-~ZST$n8+d14_=@L<ypV zDX4w~-DG69>l24^r|g|uu~KCLk1)!*IHlYi@bgq8(rYs6^zR)b$%0I-+B73GQI6+G z1cFLhmMB@}Ye&oUxT6zrbazUXK<9J(4A3~zPQJPbWkrdmQ|+z@Zi(Lp(iRmVEIGxi zbC6dkQ|Y+xlQCSQHLE5yl-toe2Pcr8l6rc5={>1W2nk#_L;5lyD-l&Nv>^o5Rns#N zMZOTB1kAByWFk}gh{FF~J_?cqE>xS(7v!SFd-m>!j*d1dZ{vcvn>MZo27|#klb{nM z3c5}IH05E-PghcCUix5Nb>{kUdb!xqsfE$8L(Au<r5aQy5a(*Y56bwRc$rKovrsNb zQdBvb3Cm=*RHTl!7eZG^ykmoAOxj+)x>V67Dqs0;MSc7-NxsVAy{rV8XW5~mMxEUQ z=aH$Lv**wLbR2(%X2uDsXo2*kJs?23sUnu~&(BwwXnB4c2T4qZ+#`PWij1N;(9u=c zY@nT$nb1=Za{-?)g}S=B3K2;sU8>C~`N}BOGgH2v)L)F|r;ZOBl$a2%fB^(9o_-R> zW3-36T#<Mcii`HPHYf-J*3!}hYgVs>rY3Je*ZTT8U@#brGYKZ*WZQ4rXHvy<a}I?~ z_0rWbN_J>K)<C|fZlR*1R4Li<xd9WD>ZFyKQkn5wLaxf>!7X*<P*TpyCO=xXZb~ej zML<!)PF<-z^t?^&RW|>*zH(+FsiegqNc@}<$%0aTUcITpqfBCY`BdT2ucsn?<?HAM z6twa=$rgg3T1v^NYb2bsUFdPD?z+ETSvhhm@}$?u(SFnYl-y(9Dxk0Ko24sVx{o0J z3|?Cl4_!H<mXg)70$Djb)6@=82v@)yvlo99`SPxJkf>E#u_&~+x5+<5S7#?M7!1al zhA?7<h&y059ajyJb2KGG?=+R!QTg4dPFRv5^y?`pACiDL*}!osKc()JiHR2ypjh-s zk6SF)MSkLc8Rg@Z@n=gSlxa+TI?6O9zn96FB5`Gf$J^k#$_(cC<*R*4rSbL!eWFU) zRS+n@JqRiC_Luhs{kuH#pPr9SB&d?7{NUtCDYELi$_veFQ>Uz0DKlkRZ6u1KqpmR+ zm89TrLJYxkN%HE43Rk)9%Y-!Jm*e{9>I8_Wx`tjd;y;INh2M*1(yKe!)B2XxEK*<D zP9O&Ypph+|OjFWJ^NfqC1F@=YHtE^*8!ebN>P3+j66SqAvNG0FvYD%ibC_?^ailWd zJ(YN!Ro<9+8hImfDde3k&nlj$04WSc`Y;WkDRjk)9!JxtZMsvo$>O0DCeu5g`hO|X z<~Yg}2H<xdN_7Y*LQ35z6QDBDLE=D7W6D<xV^_IEB?9nGVHxTO4p4G<Dk~;VrsT&Y z$QzPGPytW%5DYRGU%himbuQ|J2+yB5k$cYc@?<?Sf^AFePJRb3_7#c+fItWYWqK^P zyJ{<{lqp9t>YGKy5oMypY;sxU8&@J%r31=-US&IKS>Y-ZQYe=gRFjIN&A|(Ls?^I& zNwF-edgXi055DUHX>X4-Q_`cZfh5{wB7?3{>FQy?WT@JlkWSE^4fR(!(E&(3oXBU6 z!WxKG9|46aVfw>ot5j!48_Xrr;(XW)7+)-3fW*-t*v4c&`HGACQU*&+D{s7E^M&N| zuFpF1rIc1%r+JSs7-=Et0WEz`$Jws-A?P4mCuzFb2~9=%@fe6zMJ9Jkiyg~hyh}_# zxUcAf8!!cyoxv3`D%Yo25lX=`@p-c;73nK#<kNL20RrDQt;7c__N!w&WzP*9hbWUI z*RQYR03jA&^g+|xRjEzLA&R~#6|7HLg@Y3Bsw4#Jd=S(GOHFm&J|{r#TtbG_eqVKT zg7-v_a4P5fqMRH74g}80t5a|?g8AN7)DFA}cW${&%h#c-P@0d+NHR-#ONRKvsnm`} z%&BrqRX3`v1qpcq3YhHyH$9N_=ACcFivr_-jVBzy6VcY_z^;=;=p9Y=92@H#xO`<6 zUOZ8Rv8iP9Uew~i`tA%oaVU8vvw5)?k%&?0ODB@g`|7nh*wcqWUXzUjrrLUFBKi9i zqxU!0F#}`F9dspYChuSU-EMow+pHl})iNDN1E#8UWfP-QQfj=}j2TpvkZcr4FxHGW zBh4cQUZ?Kmmm$hh$(f&+F{x5kR>I01Zn>VGx<+T#WlPGt>y!wXA|;vfEIluQ>bxD9 zCPRpjS1qNm5c8;0CP@Lsh_)}3!X>!HQu<Q+l}Kt(#mV>t*8AV_f2(aIv|8!>yu4+b zl>EfNR|z3d4`o2rl`3$`*Fxo2I`z_QN2125Uzv<LnNnM+cED>{ke@CMCqfh|SNV<6 z`(1vPD&<n8&hgE<l9W2ZdV}MDZjz+efvR29k4-_Pp~t%#sSiV}NccA8i|*GSc-sRz zm%wKwjb(ihgA*V~L9*x7T#BA?eArDq5GxW6Y>%Ytj5w>?TPi4xyfNQX6K>*pw>5a5 zsH`O&h*g@Bc`gVe1=vQ|`a5~1dD}XVpt;BXZr90Kg%4C|TTlgY{SFbymlPzh<HoYo z5<jI*KcKKkvz_<7P;mS(73I2n9Xp+6sp5IL#H{7zRZ>vE3-O>-EPv)<Nxec7m!DTj z-SP=QMuoiqVmfuf3yV=UN#c$Y_mHb*$UF;5F$AA1DE~FrKbNvHrHGia6PEj6ClJ~} z<)hBg$(Gx(tjJ}EqNJ3u@^<h1+!DF;GICsx+Le`~GQo6;8Q;{2<fmA2`q%FtAYmye z+smkctF9tQB|klRy+vCAC3t0jG1U&>XhevFiU<<c@;yt{ETwjtoc~ooV5)Z6R#%9F z$T(p1lfp*2>_jYug#sCj1&CTuhZzeOv$=n4?`)l5nFyV2upO3`mP1XEN)4lC9z~Fp zi4I6vrXZW%_OfY6MLO3>q?&2xsckOkajo?Fs?Z4K8HIG+!Lm71ZBq+5dn%o%XTYRb zCY^qMm`3GSnhEGoAFaTbVEcLBN!vqN4RT6h8w$nBV5J_3Eay%0t0WtaPi$%Va=YVI zz|nmH)u*P6sZbJunY2zhR0ovEov&yb6eM}iWHUem^Xm5?RjTMc6ExvguK%g0r{}A0 zr}r$~NBte+5-)|=vmohrT4}o7HVRW0kHKJo^n;<kFbqaD(BH%QI~id!Nk+Z5X`YRZ zjTQOQV@za==BLjbQ{Sa8PN@^~;x>7+@d}2RVcrb7GV#LiD&CXKuQa<4OQ^4_^Jhv@ zA`mn|mQ|!8C0!XB#|r%Klt`7gK`Eb7r>>=?L`m;MgCs=c;S!QTR5PrfH`}vPi7b;N za_yY5`V%Aw!TOu}XgaGjs4}|#ah@tvD8&$~yzjC^4TRE>;NNQi8Qu2j6$s_Px{mK7 z=FMI$&q@WS9C%hIR5}^ciN9CA6ZkBjPdOxoPLdTU@z+VMDPL;7j)O=OBt0?uX@cpc zpiYdzV0gg5fcVE?REx0pZ#8+CNEw2@!)+a#zK?c5CSs<SSJ{yglOPc1@IXfvT#%ZE zk5Ku@e8qpcjHiMXNf}P5o`ur#L>T_S)%qyqho-i9epAY{rOr)}I?rU6A?lBJ&Ovo4 zc#JMm0I8R1H}AMywj39Y5`=PFyM*`@{k*(?i@qx6s*o8_Hi4?i2fhNO>f{CqBX#6g zWf%P!prv*~zcK&3gZG19R=_7vZwqqol$^9wd6F2G?g<r?R=)=T%7jy?pp;bW%U|hY z5+(aeJ=J*(y}y+iQ@_`#x@#hVHpC7?p$eo8Ng259#gN1cgTY`h7}dknq0sMP#0sHz zBw_D!+8OEF^o}anNvU{RUJwSUgt^r82)+{JIOUEWG;*g@6uR`CS~sy&ixROYt5!0Z zTb}7y?sQA~k5gW<NBOIj`#s0^b0RHPKHjCuR+(T?@=~Hmet)XMW_W&1bn)(>vgQSO z1)nsj<2?fFIl{YItgv#lEjOTcsXV2rHlW&pKWos>-=W{j4)W?ey=$$mk>@Aol6I-Q zSM*@5lv@PXg9NA~{raaKq?P`rYL;5KoRTJbuTq6eABY6a)8+DYf*l93v@+9!Ny!(D z!fQG%E*oSp7z~CPabNEGh+Y3~BV?wsREJ}#i{5FOzUwLjRldq;ivfS4Y&O{WN!6u% z7V=-}ccYFiJL|jJ5;lrdlzo$wTa4qYK~A6#Q^ZA@1KqMo&LQM0NMK9O`=uI6M|rZ( zQ-d;oeOzB@%aB=uWq&o57U9*YY{FE5O8qbzBtQI6VJfmE$9KQ2oU&;zSU>OhY^fa3 z+FB7kb>3cCieUmV<7GgR#*((@myvOldA7VSL+a;~7yo=bg|R$?(e=r&jT`}#?`<Sl zy2(%vyP?w4zjAw5TJjJo7M*FPukCjjVlBq4GS!(N{jNWB!S$WyhlJscoy#V#z~PxR z)dO@kCVNherO*Yf4Gz?2AyKr}OACAAbw)Ibx=y8(H{LeQx#aV1EzN?&fUF6}yoF5C z88R3Eu}9eYyV<NfR64Yq2$>=!w)eSBHPh>$@}M1{>L(~UDw`64bc(L8G-ZjemNLTy zl_5_#3IsuQ%jPy|?vs9JB|@asHI)aHgODn-WG15&A}@i8lwMY7JQJW#B1?5~eD$Y9 zT>SR&jxCl5N5*k{A1OsViY}1ztn|492<~^F(kxsw2q0y1Z&{&Jb@n#`1g?=hPs25J z{d8plTrSE#+Y<4j%4g~Pi-iE8R3>F#rF1>#`@ne{c!YkxnFtnDuGDt_JlslOK$<4b zzF>RB=R4LwtQ3BRqybDX0$PFcZ&{4|emVtWRX1Rw`GR*L^Tp&Vo+-T$3Mn*7R$XIA zquySflXfG-YnUTZwv)y@j5io(2;3@fS{n&7rL!qvmGAZN0lD5GnaWk=4Udckrt;HS zjnuD4?@v^hL{*7miKHnLqXXq&tAS1#An1heIDtt33Z9RbOJ+Zo7rzFmoUvCrGoMf@ zI8HDKDs2jNPPFY*9^Mj4!FmOWYq=e$&r{K#oS-7xSHA1Yby5+&D(@9qZ`Xwia~(Yv z|5a;x0wHKXocROwbsS$_^TLCaqy1Ob!wK9UDld7Bh$Fos`=&*?)v+dpOK)GbU*)at zL0U!<sLBanA1T>LSP<3QMU%o*-i47VNE}_w4m^FN2*-w#Jui#{_L}uMxcTuZ7#L4B z?{k-B;gx6S;L8tBLejYL!g{F)K7AzlK&)@vl!IFzorGr(CY$&2jtqSK`X=ZfOP)41 zil?fjff*PhdDzHJ7<5HIKbEQ!dRc0}X`8JRvenH-1l!siFAkPd=^!2;Mo?CE6qFM@ z%`c-76+dkz1Ge=lb;Sf>N=rL^-3+C-R~YfNT?ZX{y`m<x(Xvh3qw<Z@#|Mr>^{d3M zeDkb!8Y&2^%(FC5cW5F<nHcR#mi(P5`{Q}}29KSZo`&h^ypbT81`@O!l_uzORvskK zFc<XNnVucfb+jRNAXZz$=oivdk+?64q~XcPkP7g9F_G}HKFW!4bQ1rP&3md;*QE1B z;EDrvMJMt0K;UgNkp~zWPxeBpptr7@d?40&8gDSp5ZDx@FjY$LDCj=jyr|Ms&<<Mn zubC})Mld4@G&MEC_{1cPj*iLSD%1p3&rHzod6NRCtH1g-gigxiAY4atT_bbWNmm$i z*DIX_PMzwPO!w%q6WrM`7>qL#Vee+sew9YwXG>@5Xp$yh3`UY@YHSdMK4(-5=^%oV zttcL26Q4{<ShZqPxD!5jGM|k=nMPajO_MVN+G2O@*$0akFOq@xPMkOa$9qpgTWc#! zPEE=3H*Y)}7z_quKA?PH(m>dIRK0uknO>$msxf8G)V#uA%m<Jc<nyIKy%r0d{+ouC zFezdtGcARUm_;En5<dO3kt|bTGm+!s0A<CN=4SbV8yy>yX1jrbLC9rua>hhcDS9)5 z!C=faCMPH1z@a071$vW!L*_tvuU@*mj_$WZXEv7e2$C#+cnv@Tje5@JUWEGfXRQr% zOiaEQ3kh+ABa>;~W@1$7DfF~q<(d#{El7Q&WK+0w->_=sxVYH7>1-*5kBv`2V?zV1 zTDd|-4rVYIjQIo-nD+KISSl3yq*3j=)MlYjzr!pQhmRbqNR!69oWV#MrrDgj;%ici zO+phrzM>q952ikmAq=tBf;2I~uynM7CWbg$Ao)TxH+gCok}w8?!8n~zjp@&1l?Y2? z>O5$0Q~i^dlVdC-*c|D@_=s!qd5^PA&oeIHi4}&zX5Tb@+K!tK<gyO5HY8s@HvQ_( z40JRlE6LZDrbN7AeGVq3ldbpSHU}ES)qd@!<Yyc$Zp}cw$oqn2$*YhUDD|p!Iat$` zY;@DsQef9+<n9UIBH1jPY8c5PY;bGZZ#tIxe%bQ&-`R#+m`#w31p(KM%|*f{qv(W+ zhPE)NWMRset^7z6q%GcwZj#iU+Vu4E@$6z$hipnw$uUxWTYbU=ti)ed39^w-c);0I z=lPGEwJ3R#gi+<Ox=FVO0&{F{!q_p%UPu*&A{$&{ED^-zUynV$1+KdCa(L+BM`6W^ z<?zH4PeFY{JzR15%i!R_L&6Mn1YUmCmBPfn9A*NXE!-f~;g*)>s*Y3jV$2@X`8<q| zPe6NHt8qWZE<&f_$;l~ky^lgBn}xI1tn&BwiEG%?-R1u_4SW^o@38l@4%7ZPCKm>y zR$(&*nw}lo^Mo)7Otxo^?u$D>iZnq6S749!E6`CMeZiaqV_ywVK;q~T@6z3UML0N^ zbZjaIuHTe{Cl423EMWy~tsr$5EX}~92l9|Ku3D3m@z$O=lziSdo?8dM*g6eQ?3son zv8*ElfBW_v438%t2dt^i^%b@-+<nh4CDFO-?)%`+{`8OFrM4Z=)!88fNxtynR(RWu zZ-)Ese-N&|`bv@Zez@WKH^9;*iv?l(6`VZT2R%JqveSO|JAVW2{Mj!g!Tz<^zZQP{ z(>uk!9yoEl7lubh;CJ8uJ~(*jkSw#me-PG)4*t$Ne*u?Vav`*~w8;Ffz4jUy9v+70 zU)TyeckN~ZmJsrSfE_)0ToSNeam@w>hhWXB72<jvk=JkQww-X<#TUvLM$48imcNmJ zjg5`Nkz>c8udiSFl?@LML348pEMC+N{R0CqHI)~ClW^ABH5Fx=)G{@gR^RvS-wzG- z4X{MqPusTbfb-8kH*kyt=9rEQ#zKO={19EFTUWKh1~(Lx_s{{4zRx5^>4=mSbY&~> zP(tOX%T!4QgAtAfp?tM8H9>uSJ)G$6g-b8FNQPQ@_q%=ro_gw8*+Hw9$FClH0$z3P zD}+jR7#0blg}+hhYG`PLpWg9vc>3w*Wj-4>o(-2@eyKbL)8ZcztQ|Xc!EsRrl7laN z>8mor^ewl3Qyz<?uaHlkNHYK?CMIBLXjoj|J{S^Y7m3vpagCcwzO4NNf}E{hDZd{+ zdKA{KUM0!+#Kbt962uuv+18hKz)3;M`uh3=0h^Xl;&r9VG$PmibXTre0pI-g_uvzM z@kv-jCJfj}mvUHUFlrrgUaxlFQ~ySt&$zC6vWe@uR<mW4dlj_`Qlh3Klz^!UP!%lj zP?kPT4`!G>l)+#K6Jh@;c;LZDDkhO^62$4--~B#3@X#ah#ee%Mba!<MGgP-!xfEJk zTco)X!>{1?C!cx-cI?~*OP4Nz%Pzebej!vf49SAq5Y0`^@c0u?!U190?C9tarp?RY z2S5A?^aw>|?V8na`O7Yq&+(S0o|eRnkqmIbXC%A(_8*kyL|og?$v9A#tAoM8K{<%U z+1!Yef>bRjk)e+EHh5|KE_{*7<Lc{j(AL%hBcr3@daV-q<mC5a!HahkW)?94qtE}> z-~XUgUQt!mJ0MdB#qV*(LV;Ua7gWEmsS2jw&$_FX&UwO2iu%CC=1*3kibXBP9mewX zlL>Pa#qV>y7&khyr(=S2V9TLGY9`1#x93wcLH=~x!ZShs$3qh-nIK!BZ^TW(1PQTB zkf><v+kXI7uUaWO*H&R>8j;)aJI1y<(R&hB3q@pHq&s%}gd|Gn&($loqlvM<zh9W8 znuLPV1cweEkumXBEME>gckYJvj&@<LS|m+P>(`wn^WDB<rwmxUeEBkXNtm6m+{VTR zVRGCf^TyauNCMC-iKZz=^+12GAz}7iYEYH*ccg7{lRAd(L$z#td`y@nPr$~r*9G#` z^RoG$=w?WLpE?HL>1f(sCdfQpFA;(F8iP^G!1t<Lj>tfq=x>`y6s91XjyIK|5H)a# z6^PYF!l=xqzdS0BX9rFOg96FZdFO8Sf431w?as~)`G-IytD87tFD`ZNJw4t2HjIU* zu#v0;p~x3v^QMjdcUms}jxnql$s+7U9d{p+>lQ(b75TEI*9n!$(U>X$bP%o5QP@WA z3`VU&-s9raQTJ|XyJ^6qzJr#=W;P6Sv!c(KX}bxLvmkZf0=*EGj((cK@->6OV59~0 z{?%10lP^n=_>R$S<zu{0TSqk)*dom8+qdr&%B^QcNA-5|<_*$ZzCx(DGXdtM^20nX zf7vCmk3Dkqn4A=&*XK2_zBVv;V$=pC2?GcWHllp8OytXnP}0<K>XSx}Fj)pEs#3tB zr1ZGy84L#FbVVUwgvqIyo6K$RO8WYDy@cL&rEHbb828EjrNML2`yMP5<TmdxVU~ya zdxs<O`qICD4YoY_v^C95?|m11_=CSw74iAfm%k?SRk-mj*T*7Y=o;{spZt4CzD!uL z{A=*x5B-kJk5L;?69hrt@5NB%%0DK=kJ5CE%_OOdRiX8a@&Ti;Rm%2TugqNgfT3?s zA%%%Mxh%l=lnWCHyF6jjb~QQBKkh=(?>Y>^iauujW68fnTk5^Kk4!@1!1g#fQiS1& zWb2Lbzt;9-VY&c`BhC18jM@hJ(H%YBJ8QBMWlg~K9?i#j&!QqyWx@rZ)`(R5R5^5; zEZ0RR{TwV^vZz8OYi_PCrd+kS^;_SC&wTcu1KZl#+9c6Ba^z@WJDSwL@}<v3k}y=P z{_?Ls<ttYTNSHqPSN}8gSX8|}@Zpb&vX4d92T2%`LdN_8*N7e)9ru5$S&`MMT3|U& z56z{omm21>rI!&I37;OMD_nXylLBTt(YXK;FpLG3G;*Sc?Gq$vFkw<qmvtoJ=p8M> zRKmodv&oBue`Gj$M!KdmBilNSygQq+Fe$PfOE~L>jCVgy72L$#6LAFwCSYeKa1riR zD`sXMqliP`Z0eM0yU8SJ>nzmFcRfwo3K%M1lziRvo_E2Gzka<SS{wXrxBuj4QprN{ zgOf_|`^W$AzZvsG<q8QMRlP9au}+-A@Rxu6_dfZ;2`)(3lximP|K@jP9S~R!5;Lqf zV;<px)n!WOAz|iq8{aYIVe)^c&zVgTi?tLHEhuce{*VXNvbm2TlNX)q*c2p=CZYHo z9dc8d1-W@q26p$ksmy|0)a=0a-ZV&3M$UpvBk#?NvT%6Vg+l|$=H1rdz{Z{oOiZN{ zh_xZdK97v_5!K145xYWEM`ZF9n=7D7rGTUg&5Sy3e9N080s76Ge%B{JCR3!YT%l_9 z;SataHgDbtANVgHg*dqVCwKa0P$XYp`M1vne9y2<RK1YYsk$QxyYbD}vrpK3!|_b& z-lyGBp3Vw3xl&O9N%!-junl@_?pg|wPWWM}7tPdGVEI_?kZJ+Do_idQAKsIU>LV^` zYg-pw^;>TS27{42!b}<nleIxN8`|<TrBM#V=685{K=Ys?OXy^8B3}wff^ff5wr>CF zofWDR5-^${Dpz<Ms%&wglr4l#z9^6sB2h!ahNRF`AIAKkY+g5QcVk)eOr%QxuJWXL z&`(+>%`X>L@vv>CirXp^^%AKD=wtE1)@?F|5ynHp(Z}TEq>Ls#JvA*<uL3M0)AJcY zeCJ>P3QiqA3`yd*-})-3YvS+)3`Ww3L!|Wga@=>V6Ed5f&g8PGV@U!PBjv=7vkIfI zd1Kiusp98V=IN<kVPwt*oe)ONy2LV&fFbU?{~;z|^9=c1ps%Ewno@F(jKf9Zm?`yV z!lXc(Opf#zL@w+E0w-gU(o8P^>ZoR`#7u!c7InECw6-)ue}4`R9Xc$R<G}Lr`GQ<? zjR}|?X}m<!9UC(k3<ce%MBk^NcLH%@ebMh>W|K641SiTNzS{i0ed||e9IgUagiwex zyAl1=@DGU_s$ob9*$g?qkh6YW$2Y!3`EuE|A*iFhDXmQzGm*DIzF>h#!BP*tEuEg$ zv_A>p(iynOFt%)=P?UixR|*?FuAR1oM4)Eak*mu=S67!*4%J$86z%Qpz+f<H3nnw8 zZ6ItSWO^R-x9we>Hu;(f?)&9K{`burH&*ON;x$w9h1jyCY^tP_0K$MunScC~@lBnK zgyCrZTjkAuT(Gd`17RD0=z|sAmrUOmWi<>{z)VESWD}*844Wa46jTIoTpthDt;^-A znoGa{FNom#e(OE(!V53L`4^lAKltJ8kj-SF=oX=&z5#By;f?Ur)6YOhdk5^>w-45? zT_YE5{@I;($utZGV}4<KC)-Sm^t&moY2<FoW4?hZlQQ?AbNp+rdnF`|BS*_KCF2q> zn(oN4_%kgT3Bx6Pq-2KlK9y+_tj~)wzpL+2Ry>pGQGq(@+m4^{Qbb|?1GMsKC}0#! zzGjC0{sB3S`-QDrp-T|iH@)!<LRB1wV!?$U-Tq@Zb?TH%hqG5-|N7U-*jBl^I@q>t zJM7uB7Z?o2{2`8&A<SG!)6l%A-<OTh%@@$I9*Gyud{p?SfBZX016UTK8Wjx(wf^xW zZWv>3;euN^n84=%e>mZBzI0_wPpdv{<jh3YZ0=i#P$zsnoPL;eXB&l;KVi<%Sm%g8 zNCj7}&dR?uaQ)`=NM#EKV=aAu;l&r_qNVrU|4V3XZGn|5R=}RUd!@p7^yqOplEB4x zzwym)Npt0hF#DPuUYE2u!er^dn$C0@`>Iu0`IiQ8VRc6J(4Y4x=<fsDwy^YZ%P{?R zoP~3pt06n4HnOQ#_=zqX*=%rxAJ$H$8If~|ORjj8aXXs(FjPfcb*fsZ_Z&|0ii<X~ zj;d{90Ap_8^OBPK1)4LyE*Pm}bUs62QjbmhP5Ii?<xmr3*ny^k)oId#X$9sQlX(}$ zQ-}lB(d59^-U9TECVNgzf-qhoh{pGyor2NHWb;0IQ5G%{d5@5MRY1bAu`!{D^~1q~ zhx~oVjvWsifAVCXHSc3X1voifgok&}eEAg{-t)2s*wt5n-h|s4W3FAkG7Ez#tvb+D zKX)De@e?Oy)bhCieU6JVfSS4SCMTz$p`pIwxG>e+R8F;IME^ZCJq`7B^|GGxNy2dU zHdZf}iylbCeP4#TftZMycmC$Vv-;Er+NMGGe8ZbD`6hO9CnydN%fFl$MC;oNM~;Bg z)>g6abcJIId8POlfWJ(UZQzH{eYyZDRc9NCqo20p6+#rS${93KBi5qf;hj%l71LO1 z2G4wS5>g-q-sh147Y4?Y^|M0KD8{~!C)uL&{p7?HbaZyW`1m-~2@~VQ!~{%FS4X7A z^WugT#BTqo<bA^m;vx=BCEwO<rDde@h2#UV=H@sG{_xlYeB>VoVJ_f$VW!2mV(HV| zcr6VD@r<OST2_PB1{X%AupXI`8Df6kNy|E2_?O@7h?O?X1Y#pbQ5}LxJ2^E4%|g|t z>GbmG`#$cX!fAodKt!{knqi0$>d`#-@jv`%Dn4Kf36op4z{x-VbNPMc&wl1_AG!T@ z=>PPmAtT5a60z2|z7@Lu_>bXqhj*`g-yQ0KPxk9X$z;j1DPlI0qpl>!QQ$)quyU<U zBPF$+PH(2HaUn_mXDZ%y<BibUcM`6>_8PeP>$k!sms|`};syP{0}sQZMcwl5UAtxt z?A*Bv)~!1W`uh50Y#m$=?w)(^14c4H(=JYMK=L~>G6stm^+4~*lhVN$*W^Q>sTe!Y z?yk;?<MEN(*PFsfqp~FLPZXFR*a0v#D8IV~61KK`N)Yn;pemlJ3;1BreYWo=l=q95 zTqXw)LaNp+nuY@-OCi&|3|t49@%*e2b#kaEuU}(BLu6VT*`b6h%rc>qF`8B{-((in z%cA+tHT2LF(huy%cY4baiSHl#_$S~iU;2EkMYU&wYL+dc2vg^ZgTrw6hW`vpzWDdh zaM3w18w}ra3k=<QE3^o5HTt6;`P=hC_39I(Z25P;3-y~fO9FQM_kIsXL>eSwX9hgI z3nz_b<Nz$VU01>=;qp_G?WRGGZY5OP)v>0$C}zACqyynEfyx4FN5W=^k<!YQaP=#$ zf)`%c3U}Rg54_>mUJoY)!Fu+&=b^j13tsiAR|zFdsAC=NaLqMWLvL@dBwjb)@=bWx zJAVV7eCjC}7?>N?<#flf<Gq4NIIwEPa%oPSI5Y{HHm;YX3upQEo$7~{<|f#??|_kj z%^gq{7Xz;RIE*~}aVSpqTT{LOwSi~xA14dlZ~Fzv`a8-9F}J`{bJ_TcppAr?2E!)5 zI#Y*&-^T^XM+MB(+0cAV^_UL@Ob4o7`tm>cdB^SFfh4hF#WLwg?}a;xhvA`N|9cn# z%XG9s%bQ;(N!ZFezCJ4wR(H-hu>3pUfvN4=0|ZRg;eGFu<V%)`1LxPgMw%l}dpPAU zx>_Y)0igWD%G*&qqR$qc!DxF9!^EaAVnz{3z)TWcX@`=@K=<n~`Hrz7R$DlH_%M9z z5C2$n=v~kw6qj#&>)TT8!VZ7`;RDh<dE!JbEM2+;w(Zyf=Waen{5}CEg);NYUp@#! zgTuf`2B?gva4L1VI_T<bms9N*E$)Fu-QBQi<#O16;1Fap^S#2idfw+kZA%bXUnGw6 z_zIesPNs0vyH>$O#Ps9v{-ROtO?jJ+t2Lkxm|Ck5l`wo>l61(&*kY1^4UfbQs=8fR z3h41+=>EhX$WKMW>ejA=!`J_3p^go~$~(Ufxiu>x4jM1N+_)Y6y}C7XBdT8=ANdHJ z_HfJe#8<2o@zTa$aRnqB$+B&W``lE$7<V<YH{HhySnUBFgVEH5e~5vB=ly*zZhc99 zs(sJDP}VeFcyTK*lEeAuZuXz84QHPvKf49-%4Et9_?p!#<=^}x{pT2c5t|yDU}$&< zl0(UFZJtb!-OU9#12JyYoG?Ey1xljy_peFelIb0LXJh)L`)iGiq1jO_mW?5Gk|bcL zfGJ>5)i{1;H25k#-Udp->IC6J@|6*TFC=1=gbjS*>(GAFZ%E~ArWp9*7o`f;dgG07 zCIBZ0IR*zX`J9j~>YmnPc9|6B3N;Pv6Ag8qWXb~(%LJ*LANBoV1(o*$gTb&sA8AqY zMYo%f%R2DA{}Io@dp5vlxlqW%*k~3$@JI$uP6BM)xIwCS*Ix4qShQp@96Wds+FIMB zFX6Yp^F3&2Xn@AXM)>nj{0aQi|NUp^ALxgdUv(usvE?bLvi|WO{UQADM?Z!)-|!~* z&iB3#Pdu>&e)HY$ga;mc2tNAJkHG);|Gp0Ieb0N~8{hmk-1zHnh9{nU5(fGQgn2wK z&9cjuErVUVb_=y|kzAq-O>CVV9k8gU2fqKqvPtIVKWK-#`g+J_bMvCcH#Y#(*T*A& zHj{@*wKAD1bw4ipPR46hF9>c}2SGohRDh&_k?mFJhS$9MmGeHs5!a(s@y2jjq9_7} zbys8%eK+HhK}EubZ~cKJU73z{NysP(JNYM{f`$vvfu?J(gqZ-z*XZrH!_u#Q70xW+ z{Yyencw4-ntgbmmUerAv_B_}~mO9Ra*d|C*K~d@E+w_QKH`FD|{G8HXSF-~JH`&p2 zed%hf?asiGe6o43>dZi$$nwUwzZ?3G>=G6klk2oAw?F#GgYt#a)zt}CT=fd@E}?d_ z$$uPZZ0UfmwiYOeEt{4mAHNKFNLQm6(X=Pq9!;f?W!UCPj}5YOJoyjXXzh~p3&rO- zpLb!TsazI5V@sDTftJ=5c;WdMV9(xt@TzNH3C}+J92D{eSa;4@;$9jS9qa?}YSF2E z_j|WNb8`z|fZyeUJRK6Ia8zRNxbx?5-nr*U0@fgilH+8cukRFGarqUH6Qk2+LDtac z@R%?+U3tajutb!3^ypDowR#o2{N-1|^whLeK@J}|4C~Kc2ff04j>~Q3r@iT}YZqq) z0m{lS5cBa)Lc%yD{u=7z$I3H}#1-_8fRZM)-vnH?%>><gq4HAZ%CyxlbgaMcmk-MC zah>Y7Z!eqtkn|)8Bv42K@jZy9&JTa^cS1>_sym|E2?0pL6cIz9Ns^MV*#I5%75O?- z!cdfbyTI6YNjm13XFd993@%h*W=fmGeHbZ2M1-LT_cKtxRe|(3LOs0mMZ(1U7V1*y zm+*9!wK}l4Iob27D{+i#HzZGFkZ@$gt@okd{!J(fQ-~K%fTnOu*Bju`U;PUH@8|v% zKKZ}?9L_msQ)&7>f*Q{~n>t?G#iA`TLWOz5Imy?%UgW!0kjGUC3q0zUY>A$fR(;;{ z>1;F?Ag71w(`#RQ9en-fZ^%g+qhq7+@FR~vYip~Vee}E_Wm&;U*R5R(_uO+YjE_$U z#p`x?ESj>>|M1br9)o2|m&#e7h{qm(LcBK$a@7S465QG8O+4u9J1Npk!Y}T=S9Efh z3Bt7lP6;*az=8eH-PH~I_U{+w%X+z_*5XBreDYNbz;|J_Lx?z}CyaC{0!8<ajE>5P z?Q#5e<JKVwTz>f_K8g9v|M_P@!af&Dc(y$CjGrFKSJJZ}vFw}P`!4v>zkf}VFszep zv2gT3!&#XMI7whOKvN?nVKc?}z4yZ47rr28LY^t)3qjw$-aTF3s+J^D`;_U?M7~VK zNskX^;zpEw=_HP(LxhH*Fjc_xklHFTpX3XHfmnwpAaV4F_rfo>O~cls$vPu<HaYMI zuWp1}9-m4)4%kI2vvBq5JiPV~V$2r8%ewqG;G_TcqWJP+Wb%LcqhEtnYu3Pzo=ZOJ z?H|3q5uQC(ghK<#wntloAdDMx&_9}d9Iz&#y0$i+o+M1ZR3PYwbhjWxh*8nGBerkf z88`+NE>ysDOioV1U3c9rzaKhuSpFfDkD&?-JTNdIKkxd*J^rys%n&L}!2bOQ{e3uD z#8_`<KCg)@<0NKd&@kI*+s{cOPd1Y!-LKx;jSY>lNAkK;f10@Q&DRT)-}mJ8L?!b# zZ~9&M%D;WSVksc0Nd5FqNsJV3dha_S4czpecS$p(Qo$4{qjf?eh_f)&(nA=xY&MI4 z=MNkfs?`tVcQiQ;{OkXNnE=C3ocxQwfW|AX2+WSmu3UNA`+@o4Y)r})F-My2Z<C2p z_qz;x7U%>Q`Yh=KA%)1bAWhC@G!y;7wS8wS44`Rf+2j^DHoXL#wzGi2pulHZ`lrn+ znW-Lhn)!U7fMz^o`Y}b~YTliC=Mm;BmKS7LkH)W)H51Z)k0ZJHr_cNyeBi?$m4pXX zr5kR1w=e~6ln(8v+F?it6^K;<NfMHx^y>5}StkUNF(hKR3=-By1%_2XB@UNInhgpA z!;s&9*#C{r_XwON83t6i+TZs+|2M|{qQB21NUyKJAVq&&xutEDCVYUPV?G_csI<m1 zL%5Pj8H-fFXhm$l^@3>{lX{jU#>U2BaBv8gFJB6b1W}lRt1em(2ZZ`?pzkR-I^GHm zJ)3~RK;SctrpEvM4}UA4Kkvn2N?9Ih8utpKUsQgGB7;-?{qTh^{2PpokHK%g`yFuc zMHfTK&K=vJ>k8sg)GOn7izH)+kNG5E7z517#V<cD5sK;@bW(+Cq)JDl+$-3K-(0jE z`dxkd*8dQM=fC^PCaPihrvn2*fB1v%PmO#jplK6H*k}IfpJknp{M~o|gNAgMN)zR5 zCl6puuq9vmd#Dyjz}g7m!uVe*2H2ShljV-O&6K7COJ;`8LB<f`CyoVAH&=%Fj+rcn zkqVePKxPJI2R0kgvPsrSKfw5+9Tp~!@xA-yO9dM+pD#dtLmjMJy9O9DfeV)`8;0gw z9=`N{{#wptyX9NA!58lzhHS&kr$94K52$AtpEY!}4|C2Q86AVxmgdMjy~Dlt%j#7t zrQg@@fB1u<jI12B&i1`AHa;%u(c&v$^F_jN_94FX>+5H}V11a0P{FixkUDSN*VNcx z%!3lO`BK3UXuiASN8gqq9Z+q;_+F?q;Y1D$zKZ)Vzw8o8yk<gn@OZVn5t61jz!{L6 zH*W~kN!15QBdTy|MdD{v54=mMiiHTcPPqO*@&#lfNq%0g-5)GUkg^doiV!D!i=^hd z#s}C`F#VqN!;4YE(P>vUu*kZ;p<YH4$0^o~8A7z<o?ScOOP~A<?Ax~wZn@<aIQRUE zgtE{JjM_y(C`q?HHVJcq@^eg;ugYh;t0@cp<1wA!-I8xc$17p=Rj-7fJcEP1{EW?< zT`d`S?FHV#su)jfzR4E?31v1r^Dq=@yXk#PLCKc-O$inbsv1i(A#Hh>>M|GPivkHv z8l(mZn9Z3!>NpJkihqpShSazGXI<UDj{iDwOUjooD3N1zqGTgoruT`?i%jlXdY&Op z_~pb(ic_De;<PXm`J(%i#*)Q7^6xlCYhSV$7zsdJr&GBN@JE04Dfpe=dMliN{`v6P zul@isEBK{T%P5Gx_EX;<g<8Ujj%heDSj}L_)WOf}y|JVt1J_;90J)4KXHz;(-Mo@7 z1Qs7R428*5p%BNcLDNvbF#N<Iw_#*zz6ckCkrb2yR(|Fyt#R_%a4P4khxvHLO`}@} zO}=CbvDbn`_!&hUqnjwxfI5+7OP2s6In=c-g$Iwfz<vMlpp<NLE3Su3mQ}?xv0IqY zahW0P9Cz>82WPKe3j+f~azS)8xY#AOW14kmEe!K|HdF0$FWwo&X>g~7s-1wjdw%Iw zCGb6%se%OB-kWh$tFXL~1X44k$*f6_nMrvJ#(blEALIAZAOsiXc1kL=<L?Zl*N?a5 z>*Z|@En)HntJ(yq7eX^JWo$DwmMa5uj=HP^ZH<sP@Kj#5ItOdJlMOPa;()z&Qx3)x z4q?>OEG#@NS@@d|T?<L${IhzXtvv(RpI;A2167Q5T^U&1lI#tnf|9p1B!6R8yJX|! z<b+(KZ|Ty-aH_uprY5Ih<A(Lp#6CSeEn{7EcC^91{f8C|0qY&9HYS`4*?l*|MQgI~ zbn&;q$>8y`zc%$UsO$XGF^q*)Oeo$-ln-vW9|{E0(ogzbf1lSv8!{NNpxw9X_V!$; za6z(SnX};blBi@OQ(l(gzFfBLVaFh1za{mG=-b08<TwEHF^KY<c+)7@0d(hROgQeS ziiD3Z>O#T<Y`TEKQZsPzg~`uoY!@aU!F3aF4+KU$sKdWZvK>ISlwBJ4N2W^RuyVx; zalP|0O8MH=tK<?rC;LvpqMmN4ilGWNFgOGo)~|zwg0p(2VB4|yqo2a$5FFdR1^)Y6 ze;t<X{SKTMoQ6jRuYtzyjW8RGjZetQ0P{^CrlzLlXEqyekP@amnG~zA5qn_P6<LbO z<}Tt^bYQi&4kUwNg=1c7$n*VD&r0bT@DH(@{ptR=`>5Gbr7`6lyUR5B7}zSK%Ww0A zsT2dTKE6MnN+8yaug=2Fk4!?}NV0)Pui01!%R3w32hS!SEcKn2)(e6MsRUv@dvPP| zIF(Ky*5&Ov7@0^Z5NkGmhu_g&Ho3KzzAs-E46oAJ(QZ8+2LtbYWeQU7ERfIF!KEAG zE5zHMxEnt3#gEDN#if^C0xMUpg!ljPm*JJKyb!9z@=hEedF_u&7}bJ$gJCc-RSY8H zyf<y+i2l|W?4FvQ^5cNnjx{M_48}r)DU?LTWEO8@z+bBxnD~$?S68HURJ|6Y>7fsk zuxo*cF&L*akZk<XuSbV`m`h-szUJnp*y&$fe?HuO_ucUJx4#{>Y}o=2J@gR#=4)2L zduMrGlT%YtS)6YYhA(8~LUnb~W8ScLWyOeI+io+TsBIVtpB}55FZD9QIu8b;CZU<< zy?dO@UIgF#;eCLa*Vz2M?8TbR^*Rrt`y;(oY?)HnY@(*`3<l%$N2W?gxXqh4Lr+f+ zeB~=&frlS{Sdy?ezVVGPTV$(s$V&-cM-V@)4K|-I`kS`B{_Y(e8#9&>)xp$)ipXHZ zg<N~pU;RP_n;;=sN?XA~j3`XcijAo0d6fwu%xo5<>0n*evdL^FJ1m32IQ@~yR!4l$ zMEBnJz84N0I3On;-~!xTU0pC+WH17K4Uh+Hk76Od%YyBlY4h3A*P5$zzV|*G8ye(y z({e?o^!h#qqvql0j_=xi>wv#pk-X2H;KJEd4bwS~?RleVDD*t^=O`o)tLF@EdXQ<M zrphS<<6i)S!8k)OF)>+nP-L7bxNhA#ND>p{6Oe0dnRoJ#7o-y3gGe-I3w17vMCpDj z;lwSTL%qqI$%AOdLV@X-P(GMwf<z6>_y2-m<b2xz#I(=EiNZuD{XJwIrpgHe7OC6J z`0Nk8R~cY17-tCLEJHLGXs&@7kO-X3iXk!LAj$`frZIIC2xW#0`oUnxlPP0f`pDd5 zLSZl#0+hN{hDW}9VpK{c&5+u1Y9?Z(psU9Evq6cMOPu<36^){52MPN~tMWylOLPnd z<IKSLcnZ^xXMoAc$(nfoOi$-a>p@1tM1&~9OoOJ;x9|5N{#t|@Xez_hN$4Ye24f*0 zz)DKDvs-fdlJ~Le`i}PwAxPz8%syRVK4K<UE&NW`%QY%uL&Kw?PnON>s0udu642x5 zF&Ip8E>UzL73B@xTZSiHm`pfU7v|g5<iOFvWJ4|>FziCJ_&YV0e1mi}3KNT{@@Vo= zD4?g=fr-2;^G_1k*3ET}T*fD9;Ny~T*s@UUKX@2cty}@Mgz2d%sH>k{VBTy3jfnBL zaOUB>n;`QAISQ|4azYsCF%5*Nn+Sa`)4?!0;Wsxm8A%-NC~O3-z1<!Tg~6x=43CV; zwW9r{6P@7fM_e<y6nQmp9DO!T?+e>NCrsgrdhb*bh9YQoq%xcBQ>CA;(G&WxO(aZ` z@T{8a9oW@dgx=v~d+(M8fNR#*!N8>Gz!DBlRRDbXyKwgl$+M(aZmNTpMhA`zC(mZO zo3rrr{yZG$OE&KgL3FRbs2(P!qnFmI7Pz88L*3kU_!#f*E&nhGwS<nwX&9WC-4a&6 z`HBX3|8?<0S(uRg)twpebyyH=KP~}_dS{0DXW86EutTaV`Q>3fOfiz^F**@TgTy|x z--SJ=To|7=?_VLt?7y}#6B^|C`4?V-?(R-lxnj9--^!KCp}VUSk^<(r;p}xXV6ti7 zg%_L)Gr?m|JOvkBaGo*WD4go=hx;CQ6yEXH8(_9T^?K*-y`nC`^VlV}b$53q+Lmbm z?@~&>d?$Q0AyRqaL-=GDn-XnW+tKtq+ElSvPWTlk<zS&jKND3@+<;^A0aQ)KQ&44^ z#K3-UUm=x1tVqE2^%r4mI@xwy+a*-6`V8E?HTeeFxGW2coh<A;nLGhIcX1Y;KU{z( z_Dn;PSk{p#5wPR~u|gdZuy{#PkwC}n5jkobg~n3Mswtr$VTYogFMKHu4G&w_K0T3w zYC0RVWb>j<vs<d`NdN3FOhG75P&fZ;nM!j+g6~F?HA44a(bIUO%TB0lgJ_+IC58TR z7k>Ow_<g(Q6hLF03%|C(PX6wD;9>ds(T{u}upLRszklr(c;9>930GZyDa;i2Kk$eo z9*U4)d5wZ3Z4@L-$M=5p(-{#kBwx3D>o#!?UB-M(!1UL@@paH62vJjG6O2!cLlPJt zABWq-dFTWT>$qXV`dPK*T)-2z)V+(}OJOKVAHG@4sf?MY5N3u{&x0ajx^uqGYB(bw zFqNSmQ91B{yXIMr^DX-`7`2YRzJ8dRoP<-S`lUkB*4iq*56Dk^ybd2Z3eUZ;RqQLs z<%t#w4h<&1F6d0q(AZED?;)Jw87BxUh3<EvlOSpur0I3?Mf1QVuk?KN{G#fS6#8TG zg@N&C@`uj}sx}mN{QO?%=?;c68K0OC&(lj4Y4La*<e<uf`xS5<R($>rA2|YtL>dZ} z7Srg!ZTfb-ObR47NT%-m`8^f;u+F%j9<SaDhYuf><)CVI)#aB3&V?RN>mF9`YJlZn zz4Uxc^+n)swNKSg<#AYR+u4AIm<T*SB?d<0e|cXWtJ<ZT7j?pBde&^upUGXzO2BLt zQk4kEZ9qw-{e_VvCPb(8;3JR0h)@lV9z7;MdwWm9vEwIz!B{B3rHA$%I0Ow14RVPX z?4+mj)3A5{LHUlrC5hIbwN}2M>g#KR)WpH2C6iDKKvl68RC$bThN@;YFqr^N+oImN zbX$~pE6UlpR&mu8FO%j!3heMV5H$%EvCseOSLAl=^#AJb{?R7_xBc*^QuTRO<nh>+ zr{MEn_=?<q=&(m-@Dm3{3ik^Y3;|#ypWD+S3HqE!t4}(??RO9|h2`O&30N;AF*m&7 zwUQ{&yzsgI^gsTK*nUhN|LM>Ei+^qiBvWHTvBCiyDq>z)Ps?*qz;i>QhWNhte#c$+ z%6#!0ZFPzQ5=ATz>y7!`Us56QT=e>?^TBjq{*PN==bpV4d3^b6xB7X^7C{9}Q;O9- zgO4L<$Jy?86S>l#O%tgz<!2p|nWVWe)<=)g^YKlu3F5&1Lr^D7H@o-jlV+P^Cr*k{ zW(HbYT7bb=D8Mxy^>rREIPV<)JDMlk+FIe9&EbnuXEU|cIUX6PpLLyD6IkA|si~=U z6tEda<#!y%+CiB7OKdJ8_=gHaOLMb78dn{I`fW39&SZ_exx|%MTq-;9>jjBHl8K!^ zn)0XucFQ+!gX>=P3PIL9rRYvUMDBm^5qS&()2n?*&iVu~Kr`I)f=r?5jhf`pS4*Wu zVu8eonhsHoy6*vxd|-W0aZ>4Z?A%o*iZ2&L63Nqb*S<pjzWH0<hY$SjO|Yuu--hG~ zi6HL3PnbquEzSo`kVwE3&;*L)M&*HI?S?nJPLj1lLag}c@Bdz@j+esczW7yZy~2Qb zUN7>*Ho<a~!iH`9_P4#+Z!08N&%OAPZ?=5jd)_Ji^}_N<0(yi7k92t|nB$hMk#etN zcAhs4erS5rv!a(D#*GkFM+J;>STp$|lzu>x*sx(83=R&#(PJl|SrD+Lix)%DEkeTr zQVAH0Bv7Ao;3t3D0kwo;VG=Ug2AB<68)r|P&<8A6Bhm2l!i-!OGZ3r(9yLLCyr-YK zKbQL6P~TAD+eP>5Whnw@o9$jJK%#xIAVrG2D3jhUVg~aF)p`DJBuw`a)dijP6?!5Z z&ASP))6JY-eHvkt$REka#<S0oiql0F-!LRqsD2?KX%Z&$=bqo{lc&RnkND&ZaYKnL zh5^s(`xBG$eES74!}=lV!L~uQOetjuO5Caewj(NrsPy4?b#8}BrcwM;ziIm@@}=iN z$?t68hnUd0Na;TlU$OZ^+P>SAXOqb??A=p^6TV~o9p?BM#r|dk9FcW(b;>^m<4nhr zj@nrBP+@Ewpt))O>r^YMjjTsPRnb*1gsn56>J|Rwg?SrI-E==aUME|&$l$dIrTicn zK(iKh>bpYxt`OrBWm7qlxFm6<AQaN4OSIbti(@?!Gi-TjmfPdi6-f*JtuoB&j0<do z#{~)P69h}CPfc>-z<Q|yDj-QU0Uhymj1ki)=7D8+rp6~FiF)6A-r?_~ZHWSj33?t# zn)QhkGlh3QE23oD=fOshOlmS6sDvGF+h!_1lz^G!ZCV){do&$CA3(O7%O?NQz%#-y z2Fu!$?O<9OytZl;pY^kwKvsd)dPl~lTHTXy#+bJdI&z{ZIvbO})_D7LH#>>fduf|@ z?Jyo#(lnt))_<N?)YrhwLqvUjT}`N8Xo4)H5OmIFexxut@0(mp=+Z{INwU_-7Xk^_ zo#OM$|8c8tR-{0M?2fzcfp@(1&GPrJM2DZ|j6ST-ErJN*EXteS`wn9oG$SGrQHmGh z&aN)$&xQmH^FU&v)FJe#!n~r$8RqxpuiXM;!sM*Z7iU#tc{h~IwCJyercqip0@Xdt zAIYO4Xv*nd<%P=C4JGqok5ILgs)ony+O<cV(>3xp`gL7&!TCntLt7rnz%w$^M_>7? zdyy&Py%TIoR+QP&Hb~N+(cDZ5*P)X!H^hHzF40hzUOcAPZ4#Mpf>dzrS;><mg}=Hn zy(MB+w9kH>I}_e@#q3O3Vd!k0|IYbz#K`Dag>Tz@)uX+wb>2xBI^bgj2&FznA&T(m z=EpG82Q@S1a&=(x9aGT##!T<^S_XFfw|wh+0aK&D@7k;3^I!a`bVx_vEHpXBF?*qs zg=A)@AQnpHiHnzuW0APn2$S;9qCisk5C7+1B#FfGu+B)rP!+@TQ+d5c5T{Rn_Masw z(nD7$!h>nmcV*_oygk3M0eI7!%qC4$?yt5yEfp*zYe@W*39(0zM4Au!ebM8P*h&Al z<|a8o!US6$$sqU~DAOY-5?1<dJ=$RLGd(vpg;+nHE)5pQH!KpqDnJiGK`TKYR=VFL z)y;Jz{K;t-MpB3a)+IXar;ili*l@Bhq~?0@P_56weR~QpHkEANE8877XGsR`*_CYU zGQ{O8#pqsC7uzIhym3<wZhdqDo;{ds-phr_?&H@t!Ku;YLv>=%NTp`Z1uzo++SMze zmVobtObtkZs5;@~ocRXE-^x@mDIv`7%S49s_pd1}UK1pwxK5|OkACXpj()bbwn9=E z&jWn!;i8NO7KY!xECc5)N$z<<-zX#%)doCO4)xUlRh=aKn{0JfGYqS#7aoJkSe!Oc zX-z(HVfplxLQ_Qf(p6KJxWJf-7!4okhd(d@qCPo}m0+2^>**+LgCCktn1B_jfYE5l zI<YdDC2f_P4`6N5@*PEM0xpr0B-9HUqq8TScit`Kop8Pgj2T-jrjs|`Qn;GI(0H;J zvbnyTf6{?i7l2TZI?%VKr8!xjnwem7Y7&}C!ENUg7{@IyMk#Skvo^)arby}52Ai*n zx+bPUv-+;y`}Ei}&?I`FKYev3lz=q}3r1tY!g|jP{f4V0IWpXXP}_8cYbLPOc_yR< zkBNI+SlY0Caof0>x>N(B5?t~LGZ8R#Oqj{tG!xjgEsC_!a$+w<WO`V`PJk+53<l%O zLS~)`j{>gXQ4?^mP2w*@G*iOpn_QgAxJ39J<0m#YG#EPt-I_u7rTHGm^>0r{tXi?m z_qSrqEgVN12s`RN_I!TT{Y(jzt{giC|4N+?*%WCVrt%O~jV<E%f$8a)fH4^9q97!^ zFWi;PG9*a>eV*#;<3Ib0+8lWI)ya2Mxf(D(BC?t*M;M~so3_rtbZn(bGE-q1rX($H z9T?$|k?9i(tH7udrQ6uMomCjD(*2JFNF{U3vw%$l5ZkjB<@>IVF&&SHMZjz$GJTX~ z^DE$onZYo_TcFvICMsMjFq_E&uDP1kVprw!dD(HA{Ho9#{?&VGi-4_PGzoi7#`oJ> z(UnPsfK5;5WiZ?M29htl)^V#6hlH8Z>!Wq`snjh!IIeA!Z!=Fa7_|@W9w&=$SEO{z zF}|!hJC$+3^iPT?-}qRsgD87xnC+Cw7lUEPj$ON9&FWQf;NU?)77Eba)d7Qp!%$aW z2b(smhZDUg#kNk7Zm(38F1hG@SO_>YP#Xb}8*6ASNc>A0`U{<Yr~bR@Jv=!zEtj;S zM9XA?q4}G>GZ?iB`n3DcgInIICKt-ht{cmb%j9gYZ@1Qm41lO=hRB5-<cfn_h;|6o ze^dKwoC-1+XD(dnu-n+s2rbRo(kwH_4_9#X*m3!SKn1K=NPd=ST3FjX1v^iq7d|8| zM#smYwYhoT-$AIv;k&S*p?YGfzh_OIf$jaQqph{2!eT*>SHG!0Sf&c|DT6WJp!3@0 zJ?=Sx`P)q7itaP1%QT(onG3TLR@5(!511SofgNFmt6gEr*jxZjJ5BY;my2h*w9SFK zg#Al$rR-LBW?;IIY`uG0M14hjtnW_VC#JbHu`7+d@wU2fWfoczj_1}=3L4vxLcGwr zD#8tHID0(|^bbfCY;tlEnwy)TAjnp8X|~$M7kY|Uqv*Kvg@w}z_K&3(%dQ$U*3Ul) zM0n=o>cUXy3YCJ%RAO@u){h$*9hGZAsx&lzlMfhMFc=FB4$;8M=10%}QNi|n*!HRY zVb7lWZZc__z9V7<V$~~WD#)~Nz8~B=(camV0&*{Hb)Y-p**og}yuRCkLNVFAWyE*K zg$+H)lO&ubj*4y?d1pm4T(B|+>lP;*@}tzeO)1<T)vQ5{YXdG`yhwgmq^qZ=yCPj! z{3X1$|K-=_KjZ_&qLw_I9F6aLwX7>0XL<Ce6D<Vu4SYows*H_hdw<4xubNadglVl@ zn~KKdi?NX5lODIy2h2vkOnFe~YMH)GA2`ie0SiOegK63~Uk~wQ-i6@_NE|()FMRH3 z5e^O}9h=I5>o?`#ml%k3I@vZ`D>}FfmS*6toq0$aSFOp(IABj4N<Qx!&#i-RK0XOg z?3sonv8*ElfBW_p7#K@F4p>v2o5(`aae<E2A9{Ul5wOwGF=%O-|2oM9uxnshB%EO3 zR&k?6y<@|mn-=MBY!952U{kkdS|)>0b8x{Az$$-g3Y0z=Qo*Bj;Ypd_so7swh@%Ld zO_>fIj>YjSFiGF0wEO@w7-tS@CH4_2KDF@fL8UY9GDJ4m&Lhg~NVgSAeuRNeK<kL~ zV`v!+M$N;iyoX&Vi(tABrMmyAV?OnTRO$3Vjh<GeGbzt{9txoZEKEK#RmPOgRH-B| zH9ZXn4;=~kG1P+DlfhsB43CUKEnsYXye8fWQ&ZD&2I6cGrB2!Wwp7Q0+Z#$gae5~b zl`n&_kf7Fnl+OZ`?qN;ALSP!Y@Vv=iD@@%oeb*H?3jJA&@B#DIFbiXNrfqXEdfKyh zzswgGNX2!ZaET2hWmzW+=byhB7!1bzp}Dy>6tG%Yh6sTnDC+9sdwj%wXWCx!dIzK4 z(c=^2(AL@-IrUzdUZ}Z-2e*s`0oR)e>8ODR*%fskn;hurZyN$?n_D@lMQyK20V@_O z<HOi8rxh`pBOzI|wY13DmXdU3Gt#f2MQrQt=>i6WF~7jD8MT1ve4!?Y1J)s5MHrDd zq_J}LdGw^HmgbgN<(e!QX;?BCjHKZyTBUo{tz5sFej*%;GNx~%X%+PRbQMkg*(TqF zD_{U+`3~&l%5;J>y|*vf2Y2Pl<*-tG_74m~S7!$>7>wFMu|`5>)JpuSVzCf=2BgjR z)<%9zBWoQhme=(3lnhQAm8aUT=U*r}@iQ2;2)xHpl<}Vi$Egs1lnQ1dQzq3*|4omN zQjzt1b)FRNgb(HskzQ%je#dZPX@JQWgHd~!Nx;!G&`@7j6DCM+CT6^Pg!$gu(%DAz z>a)DR(wY&KH~o&QF-(gNj+HP*tpV@Lfq@~p<QHf@Uy1<fCP-cBqW+Q!^!c$3TB7{r zO;$ui>^NYi?O~O2EB3>jBbRlcy(xu2tm5>~?#aN4_T*!AAue8?mDARfL~DZ=i1n(~ z$;U)m)GSO+;+{_<ZwyZR+H>mQvNg$$x|<ukYuB2Bt5SB3JN&7<3orYVQ!pREGyUt| zY=Jjl77TbgJ~0Vxt@E#xObWuFud6v9$O}_wp;(ypOh^+<9fUsOR#3|!#Wg3I{Cm;7 zjGY~#;=OMeY^%CZpE7zZF1-aBnN;;w9i~31K<`9pw0>Q=v-$PdltX%)O@OeOqV)Qj z>Js*8tBb9^`Z=1~AuO$KI<rBqr%nCQ%b@izou@un(CbpELdkzYuc`{Ts=`j_?WWF^ zE(xTUYZ|cFR4|oqR6FTyqn8<bF>bw*rp(+B>3lw3fLLPEF`ndExEN1IbbJY0H|k3x z<enx`?}WYPa)NwefY6@i<gY`0*71VgHbe3d)m^(N3#$^okSbVe()R}<TV)du3=T<O z1)Q;mCiNA|m%)h>y>Qmr)j~<~h)v(AewYxXX61@yP)oq(J26z*h%L>v5tpo1obZu& z)j7!#Qd>u+zEdJb_v1`VJuX_<fZ7dlFQIh_+o#uy<}3FTWkL}(8WO;CeySr?go7#^ zw&M_CW?-FQ==)3r!`8p)W-dLQE%XN^3v{1eNBuau&t@W{>1_2jotv#3({rU08ykVq z&)3uzwmgGDCDeAu^Y`+2n76HTZ`sI?tvm%2LABkFDvfEIsofQ-UX0tsfK9|p9cQ`` z<_fr+&qxY^SnFE>_79{Jh;>y*4o0UiYBnT_#yS^TL|>jpdt{nid9SCDH%8f?oOWR% z**L6HZEO&Ek4>f&h_wn9HYf-fb|Pz5uardV$kAhP{<)iBS||;pqoaa&O~KCHd!Tax zncGnTs|6B<^=NLIe-f52O$Mm1PbMgAwdbSWzpX9J6?xbO&ZZ9BM9g%;r++scuXnyW zS)?Q^K&mwLhAud+(wNAP>HPHHdgodVQ8B?pK4{zN`G%1uTfOMMaMBW9?`q0H*k;k% z+KT2w376^oEB7hF<$Zcf3YF5jrOjm4qkp`C$l8dbNgdSBA?lbo&!paFvEqQa7OBn8 zq!c~YcG2g9d@&dc1>+MF(xj8gWCdAhg5}GWzzbWq!K5HwIJ38>rwf)YSquva)hxM_ zCUV)@s0Y3hGui6RkEYH{!8YPHsTK-j;}aErE-Ib=+f=S8tu3A2FX(yO(%R}G&yAeB zp06p8rmZ~Fd4|<lKSxtqQyZDuk`hF{4fOJCd7Ad?>GU=YBNe7Lx3w=NGPE398&c9{ zssklG-tm-p>DL+jZJv5s+8A;l+6b*}zrIaRZ@Q-R7<#|z>2!oDU~xw=CYi2MDPt1G zV3=^urVal0v)7*`KfAkv;Z)Y0wFYVxT`iR_nOwFu7UQl3|1U(fgw*!VwT<ZGzWnP- z%Mj`Ln#$D`DN{LgmMJZp=AqZaRA=3!h=-^SS(zBA>O%MFZK0osN^erf!qhFjqgBV7 z&cimdj*<^kdcCex^;73&N^2r;w)4^JOVjH0vXKi~Z@ryN_oRNDo*zBma(Z9jEFDXa zk^g$%$)^msiutI-+V!##CJK|9SH2G`$*$|#&D$o<6owf&;VbBpv)ZN~>+J^ygOLK7 zg)Q~bznh=<cleao<tpD3`9i*`z@upaV{g?03B&gvI^oyPLK%$e%)D~U<VSU$*=$+G z&_i^XIvD+Z?ie~aQ`ri(z?ZtSI9{Wm^LUWp;r=j!W9s-#+fAP|FVi{cB#NfD<r$^A zn6}&M8<q#H2PGWT+1ykoQ$51UFqISMn5grh<V#X2CunXA97lF3r+a^&YBxjO^n1ZE z5NmpF2%UV<y4paGiAt-Gi<6;=>8cZ=CtxCDrk4hT!AK6Z=9rH_=l5DbV?zVf0+9G* zv$G>#vq`7k!)g|!O|^*1%cNdKsc5F(!A{)n|AU`Ob<t<A(LBrh<Wa_QY=kk4^w<c7 zYw74soq1TNZ-P#m^iDkvF)%50rsHgNpyG<D{|~FXp1=9i8<eQ)rJEXEmykWBbUEbw zgBe)wA6p&cD6{%Cwu?*VXCclr8%qJxi5R^gwo2Mgioswc16&JrU~mwYEM5e?Cr`n$ zrHf%vPdCgbcJJ9IKQW9-Hj|a)aIRy5;kt`YKl{8a`()oK=xA@3zE6wh!Iy5w&fU=5 z+yn;>9fpg}KM%IPv;)pMYqh*))ncaa(!Ki*!06bxG}A~Gt~3jB)v6U0MA<|d%%rK( z>E2YgFq5C3wxn$61V$-2YMX9~3la-ezSk$CqqgZBC}<gRJMXA8wtQ?IxDBSZw)L}g zyKSH8{4*9Ob$XmSRxd|KR5|5z%FKw@v&^Af<w#FRIrz7STe?R|#z&RL;!jq2Z&cRP zRA<}06-B&g9;SU^h;+i&30K$&nT}VLV=x%WVrXbs5{icUdeN!oV0dH%<{RzpZ7?;R zhZW0~!pP_d3=9myTmxqsVm+|zrAwATYio-n8r3WXloYzUI^~jX%a<;NqsNZJ5^=qb z9Xm0j2>oF%cGGCyuR~`Cba!<KQtXw9#A{-55(Wo`tmGuhp<Vsf9kcbi*witamy<D* zB>b&>y-ay$GE^y2OXmemSNb{VWrrytw!Y3r<f33|zpy;RR2@oE=|0-t^xBx(-E{60 zrbz+xw|D*NJpk5nD7jKj_LOv)$fL{$i5kv&biA|_Cb37aqlqNNX@fA%qaUYV*Qj<4 zlruCuYGp^XlBQ9Nse+~#gxc2G(FP0#BN<>PdQvD~IBOD@h#3@wZ1JKVm`@B3kHEN4 zo4UI@r4ogtr@gHe<{C&&amk(ICr-+^YGY$#vc2ZIeI_TTWZ=^N{(e}pbP41IIXxx5 zuUxS__Dh-~?qHU_mk%C30$I_CH8wTM`=qI<N!G~~B(S5Sy&_*-1q*wpn~u@9nL0!I zdvIvPPp3Pc2Z^BSw9V&Q?qHR&;#hro!iY;$ryK^_FVg&JT}<s_>%il7<|@yy@DO2S zLZu7Kzmg2PemheIQeCSu5jHDG6=iGgU!I?rcQIYBpnw7vClrQ_9P8=ydYRJGwlKw; zQ`=4VoDC5K%o|ncm9@#1w1PTt+guZ{$-E2WQ;;}1nj9FLc40c<^@ed^&@S?R>`+0* zzDg2HTOBy7D+7-mOkM>ze`!`m!r$JTeBRfbm4h83-`>$=+XG{+UA{64gX1o^$!0C% zi#63(-?=gvaS%lU^iClxou0--r1bPEMi*7_^g+2QKP)dwz^E!h`x@M?wy8W-zl6xD znP)y3D3i2czoplK5(pqfLML?PwDgni)6-K@Ld#O;SDDsKENLF*<H`hw9<MeB)0Ep? zr7^Wru)QkFqsQyVdiRD4Hts>wl{bP`abj)^RuatZ6OS~Rwngo?ZL^WLI7C)Q#5ASg zR8ZT4q&{hbgfZ^90#<E01*-#|F$PLaI&bBpdrFXvv4ja25)RBUX(EFd&*KX*oxCyM zp{eBaZYz;zT+txuK&-ji2tCC(%@Fr}s;exv0diPgVS{b`({wCVv-IO>M_kUwOQQ&v zS2iVNPzpKV5d&%da!S9kpDvdBZ^wZ^o<Sm{eye=E-&Do%>RL|k@c;)Z`9P2W&|}px z^mw|D)}@jl>c>@@@8~(`Wh+t#B#u^jTkv`W&xPJ!rZ5mxVXQ1$)=yoNz}a})D#({A z7Rn9lIom^UF>NQlb9)$Bvfa1UaFnR$N!|Bx^NfrGMhmv>1E_Rj^FLiTl?Y&XlIZAL zEAfUH3<e`j#C@sTtQB$cp~soba=IxdObxK93AB?c|5Yk7l58Mx$dr{8^at_;k|3$_ z{K51-E7%v6O@I*SkjvzxlFZP&OyrB!x3Xh5t47LHMagHdU1*EBR#O}K1wmlGx`Vpu zIQ^KQ0vMLQd|st=KxQmD<?mk3E-fZlAu#`zIb$9cKQz;}IONNg*2rBT_#D_ms_1DG zRKO}9B(uC6a<QE9Wk?doj-P<u-jlFy-C7tM9fQWECK$(=K0-!XXi>u%3<e`9(D!f{ z5i`Hb&D-@nO`VykudsDal^q@UpNf#l-=%-DD)6dHR5iyt!0}^-xqd)S*Wj=%{dP*- zl_w6A>t9yaRMt*Om?`Hk7y-J32ht%S#5&0|0W)CG9Gg)DOQg+c<VX=s{8M#wN=lMP zINd;<JS&|WI0yRM$&?9@3zcNtKaPlL(xhBEkH9qq!!<4COWRY$=2G<zsA8oyE7yn2 zW-T`uX?ycMW+MEy`^`k8D9Kg(OwWqxe%1#hVWhYk5*A6o!kDA!p*C%|rJD_KS~Z%E z4jeozr&XUg*(cPn9JI8gu_82s!C=fk!rrC&yIQ5E9gU4Rnexy(J6+`oBX@co5VnZ! zBqfw+hEKdG87sQw-d@g*%V6-<0#RQ4-GsQ5)623PRRS@wDN+U*08OpY6&+o9@=a^% zJCw<kzr9p$xh=~by;nOI$|vpZR-~&e7?tytZApolQ&yn@q_K3azB=WU;y(C9u5>*e z@QG3Bq8Qqd)B|&LgG~kWT)ay7q)LU6kfy~_8G)t`QJwrtK?Vn=^z?VwHmW|-?KUDu zfBPgJf&&VhLS}j%byU3=w`n__-C;@_ry|v2)ym~CGBO53Lql@Pc}sJ%d_e1RY?5R! z7-t@$%p<1Xx~gF6xb!k;I+JQ<%7b>AdMD+SI%dBgQDgx7aa1d$VuGp#jRoe)ZI0UK zc%(or)a>P*DIKpAtTGt`Aau_sN8a9&#6(C%`PfoM{&q!fJQpt|1vx&^aZ8(h-zIrZ zC4!?!lIy7A2x+7w#VJ>xwy&yt=~sZPVLEc|fooi@X`r-<<New=L9XRj2K;iU>IQVb zIwIp3NfeNKC5l5R==ZEXA%rSjI$;arA~c<e)7XyHiDTS>2_ifYD+Ea{g=r)eCcB00 zn+Y(k5B~LZcUHa58iT=LEHv0&{w7mMSlTFZMLQr0+xms3a%|hogwK&A%JbtYktXdp zRJ+viP$Fomp5FVP=rq1!1wP>@lZ#AwU$BDYYnjBkG@Vj!Ttc#(GFfuz0W?3L<e^+A z$0uq~J~zjKu%1%YotE#II(l`TNg0k4BzB=?X(Y)ZZK_Vw-$&z(seP(W8VPp{gwQld z(C<^4MvqZvQpNP+^!v}0M(s1LZ)(cB+ywDWjyl4f@BxgYoj4Uy$IYg4aS%qnu-t}R zvdWuMZ{<w4gqONg&9tc5f$3thZPwM~MIK(>mONQ)677ItDALFq+oZDrppbCh&86V% z^;!EMW2WGFA&fL&B334%RgJa5G`csv$K#Z(t6!$R!&I)XV7O57Ly|{;9|x+U&Y6HY z$;%%Cb=OG-B@dKD1W1WN1ytL0!eu-c+^@pBm&(TPsvJt(Xubhb1rSi86iIVkqKzq% zjK{lfS&gIRsmhV^P0CZ5FFL{ckn>c8ik4&Ro&35R;$*4cCbDF@zf3R@F}gjBXzAsI zku_b((g~x9+suUPgvJ4*uu0UWhtl>iceKxaKt=|G!8jdJ4bf8RP48`6XJb2Fw|-Dl zPuRfQ)>jM+40(B3f?JkS1AtS}(bAY?PAL!b@le&au>Gbsv~8>Q8X3!nlDV*ySHRau zBZbunAJ+Pn@(ingTs6paZ$;&ym#fa#M1=Hm;t(PenbNnLj<KEVOo*34EDgt|Y?;yl zsqkzn#$YfQj3B}YQk>(|cM6rC&6;3S!4&z@J3T~Hl=48|KvXK4R(+Z}Nm`Cxo=Mrz z%QcY!lgMBa9Q4jUjELFD2W>wEooED(HMEOrI|aR;4ugr{&^p=FApKY!w&P6aZaOxM z_}h*RBXVK)Jk8s*zii$v*F*O=qvgzmY**!kZ(^Bhd-O$`{YZepU@*>Pm`KA+jzffz zi>NXvX`>ys-bvBpb*l&^QNd1M{%h?tO)7}&EFC9cR<s?fK5gw_>f;om{Ew<tP)tPC zHk(dA2NPkW>Gkq;(q*cnshp_(U+vS=(%<@bTitBr*p`QW9rZS)b=KQJ|E;THrZ%GY zwdwq;MQkr7+r{?Txs}m+vsY$VKL&%rU}%`>h?^ekgeW-byQv)8tTU6!M9b65un`_F zX(fTs>!#0C%Ve|G==-+LpVp&Xex>^i-LBU?t|Atv4tjYe!b8=WID{{%4Q5jgO<V1` zR=dweOiiSV?pM&2J^j;k-RXT+4T+A6*aXbPLg|BMV!YlqHbpWR3`UaBNlrD;6|1m! zvrg1Zow1F4nL2cup4Nx%H?1>i>U^_;aOtKqQ+ZL)iIYwYsZvMNg&iM8I&FlP?l%z% z1yft=>J3e6s-r?R<>>jD$}#N=+a5Rnu=?pF*!FBhMN6;|EWJGa_e{wbVn%^j!yYcv zHt%?rz!(e$BMI2Zg{`Bt9c$_Uqlk-X0In+vVaM1yE?WnhEln#{`wNBgBD{*Mgq?Sk z@)M`7w*Bh2jezI`&KBap^jj~>)Yc|-CXD!o!PNFDZ(5G&Tx}*zT6g{WhMgnbr<bb} zPX*e(w(DWKE;iy7=laBXUcwNqfN3Xbx(KGqRP#VH7z_rZI+zHIX}j7N_0Fbwn9}LT z#8q{o6d&FBT)`G~+~llY9^{9UEt^UeMj%YvP3;!e=b4Vt%Lya1Iyo|Zn#$ALK>w{@ zM-y!K89ml^PBfkA{7m<To^O<DrhbR{XX)*wmuVuArsqYk$7~T9T*>Cts48G$n)Hvs zU@#cT!$g9rAzx~rf=zi)B+1lK+w!G-fX(!ywmYToDqmE*l<H+t!PK!jK?5-RG3m$Y zB+t}7aY%!SfS5>)o-T^$*a#7w@oB58UPoKo)9XS>h^aiiPNwT`I;S`U&;-4W>2_0n zXuaZ)B2)gf4Z^Oke*A0^Nx*CkPq%r0P?0W*$ubxW24i*zdq*Q?^WL?QI$I}2RVvki z>t-@aY)Tva?JB)eGctzQU{vwYi4`RtwvLk4#YVjJj+maC=~%}JSAz8V+x~3zjI+;n zADBpr9(uw=;`DNDgfNUShMfaF-c~+MN7LHQ*Y-)T8_m;XlGLGJLzCh-Q^XF$N?~HB zaoBB3a}(rvtwsie!AK1zQ;QDUe!WAo5fweHX}hgVn(y><9>&HeDo9VZG^0_khp7xq zi+*9IPEkM4sCJ?G$LVC_ri)vy?O0V-oV>z_r-@j`IkuW|6ilSqbUpR^%vP_saH=54 hwtf1!+3G%9{Qt?(R-9PJH*Wv{002ovPDHLkV1jm-;4lCH literal 0 HcmV?d00001 diff --git a/apps/meteor/server/startup/migrations/v302.ts b/apps/meteor/server/startup/migrations/v302.ts new file mode 100644 index 000000000000..f7034508f499 --- /dev/null +++ b/apps/meteor/server/startup/migrations/v302.ts @@ -0,0 +1,9 @@ +import { addMigration } from '../../lib/migrations'; +import { upsertPermissions } from '../../../app/authorization/server/functions/upsertPermissions'; + +addMigration({ + version: 302, + async up() { + await upsertPermissions(); + }, +}); diff --git a/apps/meteor/tests/e2e/administration-menu.spec.ts b/apps/meteor/tests/e2e/administration-menu.spec.ts index 4e630c60e16e..e105b4a2a0d4 100644 --- a/apps/meteor/tests/e2e/administration-menu.spec.ts +++ b/apps/meteor/tests/e2e/administration-menu.spec.ts @@ -25,7 +25,7 @@ test.describe.serial('administration-menu', () => { test.skip(!IS_EE, 'Enterprise only'); await poHomeDiscussion.sidenav.openAdministrationByLabel('Workspace'); - await expect(page).toHaveURL('admin/info'); + await expect(page).toHaveURL('admin/workspace'); }); test('expect open omnichannel page', async ({ page }) => { diff --git a/apps/meteor/tests/e2e/administration.spec.ts b/apps/meteor/tests/e2e/administration.spec.ts index b8cbe536b4f4..0f350e56eec8 100644 --- a/apps/meteor/tests/e2e/administration.spec.ts +++ b/apps/meteor/tests/e2e/administration.spec.ts @@ -14,9 +14,9 @@ test.describe.parallel('administration', () => { poAdmin = new Admin(page); }); - test.describe('Info', () => { + test.describe('Workspace', () => { test.beforeEach(async ({ page }) => { - await page.goto('/admin/info'); + await page.goto('/admin/workspace'); }); test('expect download info as JSON', async ({ page }) => { From 7070f00b05382e29a56ba7a2fa9d68116c86ef00 Mon Sep 17 00:00:00 2001 From: Rafael Tapia <rafael.tapia@rocket.chat> Date: Fri, 21 Jul 2023 08:24:26 -0300 Subject: [PATCH 139/149] feat: return all broken password policies at once (#29593) --- .changeset/soft-yaks-matter.md | 5 + .../app/lib/server/lib/PasswordPolicyClass.js | 59 +++-- apps/meteor/tests/end-to-end/api/01-users.js | 216 +++++++++++++++++- 3 files changed, 255 insertions(+), 25 deletions(-) create mode 100644 .changeset/soft-yaks-matter.md diff --git a/.changeset/soft-yaks-matter.md b/.changeset/soft-yaks-matter.md new file mode 100644 index 000000000000..c326eb7dca70 --- /dev/null +++ b/.changeset/soft-yaks-matter.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": minor +--- + +feat: return all broken password policies at once diff --git a/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js b/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js index dd099452e732..9839845ea549 100644 --- a/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js +++ b/apps/meteor/app/lib/server/lib/PasswordPolicyClass.js @@ -43,15 +43,16 @@ class PasswordPolicy { return this._forbidRepeatingCharactersCount; } - error(error, message) { + error(error, message, reasons) { if (this.throwError) { - throw new Meteor.Error(error, message); + throw new Meteor.Error(error, message, reasons); } return false; } validate(password) { + const reasons = []; if (typeof password !== 'string' || !password.trim().length) { return this.error('error-password-policy-not-met', "The password provided does not meet the server's password policy."); } @@ -61,46 +62,56 @@ class PasswordPolicy { } if (this.minLength >= 1 && password.length < this.minLength) { - return this.error('error-password-policy-not-met-minLength', 'The password does not meet the minimum length password policy.'); + reasons.push({ + error: 'error-password-policy-not-met-minLength', + message: 'The password does not meet the minimum length password policy.', + }); } if (this.maxLength >= 1 && password.length > this.maxLength) { - return this.error('error-password-policy-not-met-maxLength', 'The password does not meet the maximum length password policy.'); + reasons.push({ + error: 'error-password-policy-not-met-maxLength', + message: 'The password does not meet the maximum length password policy.', + }); } if (this.forbidRepeatingCharacters && this.regex.forbiddingRepeatingCharacters.test(password)) { - return this.error( - 'error-password-policy-not-met-repeatingCharacters', - 'The password contains repeating characters which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-repeatingCharacters', + message: 'The password contains repeating characters which is against the password policy.', + }); } if (this.mustContainAtLeastOneLowercase && !this.regex.mustContainAtLeastOneLowercase.test(password)) { - return this.error( - 'error-password-policy-not-met-oneLowercase', - 'The password does not contain at least one lowercase character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneLowercase', + message: 'The password does not contain at least one lowercase character which is against the password policy.', + }); } if (this.mustContainAtLeastOneUppercase && !this.regex.mustContainAtLeastOneUppercase.test(password)) { - return this.error( - 'error-password-policy-not-met-oneUppercase', - 'The password does not contain at least one uppercase character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneUppercase', + message: 'The password does not contain at least one uppercase character which is against the password policy.', + }); } if (this.mustContainAtLeastOneNumber && !this.regex.mustContainAtLeastOneNumber.test(password)) { - return this.error( - 'error-password-policy-not-met-oneNumber', - 'The password does not contain at least one numerical character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneNumber', + message: 'The password does not contain at least one numerical character which is against the password policy.', + }); } if (this.mustContainAtLeastOneSpecialCharacter && !this.regex.mustContainAtLeastOneSpecialCharacter.test(password)) { - return this.error( - 'error-password-policy-not-met-oneSpecial', - 'The password does not contain at least one special character which is against the password policy.', - ); + reasons.push({ + error: 'error-password-policy-not-met-oneSpecial', + message: 'The password does not contain at least one special character which is against the password policy.', + }); + } + + if (reasons.length) { + return this.error('error-password-policy-not-met', `The password provided does not meet the server's password policy.`, reasons); } return true; 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 840e1a1c97eb..ee132a71ebde 100644 --- a/apps/meteor/tests/end-to-end/api/01-users.js +++ b/apps/meteor/tests/end-to-end/api/01-users.js @@ -20,6 +20,7 @@ import { customFieldText, clearCustomFields, setCustomFields } from '../../data/ import { updatePermission, updateSetting } from '../../data/permissions.helper'; import { createUser, login, deleteUser, getUserStatus } from '../../data/users.helper.js'; import { createRoom } from '../../data/rooms.helper'; +import { sleep } from '../../../lib/utils/sleep'; async function createChannel(userCredentials, name) { const res = await request.post(api('channels.create')).set(userCredentials).send({ @@ -1508,6 +1509,7 @@ describe('[Users]', function () { }); const newPassword = `${password}test`; + const currentPassword = crypto.createHash('sha256').update(password, 'utf8').digest('hex'); const editedUsername = `basicInfo.name${+new Date()}`; const editedName = `basic-info-test-name${+new Date()}`; const editedEmail = `test${+new Date()}@mail.com`; @@ -1538,7 +1540,7 @@ describe('[Users]', function () { data: { name: editedName, username: editedUsername, - currentPassword: crypto.createHash('sha256').update(password, 'utf8').digest('hex'), + currentPassword, newPassword, }, }) @@ -1764,6 +1766,218 @@ describe('[Users]', function () { await updateSetting('Accounts_AllowPasswordChange', true); }); + + describe('[Password Policy]', () => { + before(async () => { + await updateSetting('Accounts_Password_Policy_Enabled', true); + await updateSetting('Accounts_TwoFactorAuthentication_Enabled', false); + + await sleep(500); + }); + + after(async () => { + await updateSetting('Accounts_Password_Policy_Enabled', false); + await updateSetting('Accounts_TwoFactorAuthentication_Enabled', true); + }); + + it('should throw an error if the password length is less than the minimum length', async () => { + const expectedError = { + error: 'error-password-policy-not-met-minLength', + message: 'The password does not meet the minimum length password policy.', + }; + + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: '2', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.errorType).to.be.equal('error-password-policy-not-met'); + expect(res.body.details).to.be.an('array').that.deep.includes(expectedError); + }) + .expect(400); + }); + + it('should throw an error if the password length is greater than the maximum length', async () => { + await updateSetting('Accounts_Password_Policy_MaxLength', 5); + await sleep(500); + + const expectedError = { + error: 'error-password-policy-not-met-maxLength', + message: 'The password does not meet the maximum length password policy.', + }; + + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: 'Abc@12345678', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.errorType).to.be.equal('error-password-policy-not-met'); + expect(res.body.details).to.be.an('array').that.deep.includes(expectedError); + }) + .expect(400); + + await updateSetting('Accounts_Password_Policy_MaxLength', -1); + }); + + it('should throw an error if the password contains repeating characters', async () => { + const expectedError = { + error: 'error-password-policy-not-met-repeatingCharacters', + message: 'The password contains repeating characters which is against the password policy.', + }; + + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: 'A@123aaaa', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.errorType).to.be.equal('error-password-policy-not-met'); + expect(res.body.details).to.be.an('array').that.deep.includes(expectedError); + }) + .expect(400); + }); + + it('should throw an error if the password does not contain at least one lowercase character', async () => { + const expectedError = { + error: 'error-password-policy-not-met-oneLowercase', + message: 'The password does not contain at least one lowercase character which is against the password policy.', + }; + + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: 'PASSWORD@123', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.errorType).to.be.equal('error-password-policy-not-met'); + expect(res.body.details).to.be.an('array').that.deep.includes(expectedError); + }) + .expect(400); + }); + + it('should throw an error if the password does not contain at least one uppercase character', async () => { + const expectedError = { + error: 'error-password-policy-not-met-oneUppercase', + message: 'The password does not contain at least one uppercase character which is against the password policy.', + }; + + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: 'password@123', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.errorType).to.be.equal('error-password-policy-not-met'); + expect(res.body.details).to.be.an('array').that.deep.includes(expectedError); + }) + .expect(400); + }); + + it('should throw an error if the password does not contain at least one numerical character', async () => { + const expectedError = { + error: 'error-password-policy-not-met-oneNumber', + message: 'The password does not contain at least one numerical character which is against the password policy.', + }; + + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: 'Password@', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.errorType).to.be.equal('error-password-policy-not-met'); + expect(res.body.details).to.be.an('array').that.deep.includes(expectedError); + }) + .expect(400); + }); + + it('should throw an error if the password does not contain at least one special character', async () => { + const expectedError = { + error: 'error-password-policy-not-met-oneSpecial', + message: 'The password does not contain at least one special character which is against the password policy.', + }; + + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: 'Password123', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.errorType).to.be.equal('error-password-policy-not-met'); + expect(res.body.details).to.be.an('array').that.deep.includes(expectedError); + }) + .expect(400); + }); + + it('should be able to update if the password meets all the validation rules', async () => { + await request + .post(api('users.updateOwnBasicInfo')) + .set(userCredentials) + .send({ + data: { + currentPassword, + newPassword: '123Abc@!', + }, + }) + .expect('Content-Type', 'application/json') + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('user'); + }) + .expect(200); + }); + }); }); // TODO check for all response fields From f621587bfa7f7483538d43b4f641abfeca6e95fd Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Fri, 21 Jul 2023 10:36:41 -0300 Subject: [PATCH 140/149] chore: Change `Icon` size inside `Button` (#29863) --- .../components/InfoPanel/InfoPanelAction.tsx | 4 +-- apps/meteor/client/components/TextCopy.tsx | 7 ++-- .../components/UserInfo/UserInfoAction.tsx | 5 ++- .../components/avatar/RoomAvatarEditor.tsx | 10 +++--- .../message/content/actions/MessageAction.tsx | 5 ++- .../client/sidebar/footer/voip/VoipFooter.tsx | 24 +++++++------ .../preferences/PreferencesMyDataSection.tsx | 8 ++--- .../account/profile/AccountProfilePage.tsx | 5 ++- .../client/views/admin/cloud/CopyStep.tsx | 6 ++-- .../components/RegisterWorkspaceMenu.tsx | 8 ++--- .../cloud/modals/RegisteredWorkspaceModal.tsx | 5 ++- .../admin/customEmoji/AddCustomEmoji.tsx | 1 + .../admin/customEmoji/EditCustomEmoji.tsx | 5 ++- .../admin/customSounds/AddCustomSound.tsx | 1 + .../views/admin/customSounds/EditSound.tsx | 5 ++- .../customUserStatus/CustomUserStatusForm.tsx | 5 ++- .../CustomUserStatusService.tsx | 7 ++-- .../views/admin/emailInbox/EmailInboxPage.tsx | 5 ++- .../views/admin/emailInbox/SendTestButton.tsx | 9 ++--- .../views/admin/import/NewImportPage.js | 5 ++- .../views/admin/import/PrepareImportPage.js | 6 ++-- .../views/admin/info/InformationRoute.tsx | 6 ++-- .../views/admin/info/OfflineLicenseModal.tsx | 5 ++- .../integrations/edit/EditIntegrationsPage.js | 6 ++-- .../edit/OutgoingWebhookHistoryPage.tsx | 10 +++--- .../integrations/new/NewIntegrationsPage.js | 6 ++-- .../admin/moderation/MessageContextFooter.tsx | 13 +++++-- .../views/admin/oauthApps/EditOauthApp.tsx | 5 ++- .../views/admin/oauthApps/OAuthAppsPage.tsx | 5 ++- .../PermissionsTable/RoleHeader.tsx | 9 ++--- .../UsersInRoleTable/UsersInRoleTableRow.tsx | 1 + .../client/views/admin/rooms/EditRoom.tsx | 5 ++- .../settings/inputs/AssetSettingInput.tsx | 5 ++- .../UpgradePageError/UpgradePageError.tsx | 8 ++--- .../client/views/admin/users/InviteUsers.tsx | 5 ++- .../client/views/admin/users/UsersPage.tsx | 10 +++--- .../client/views/home/HomePageHeader.tsx | 6 ++-- .../views/home/cards/CustomContentCard.tsx | 5 +-- .../tabs/AppStatus/AppStatus.tsx | 13 +++---- .../views/marketplace/AppInstallPage.js | 3 +- .../client/views/marketplace/AppMenu.js | 14 ++++---- .../AppsPage/AppsPageConnectionError.tsx | 5 ++- apps/meteor/client/views/meet/MeetPage.tsx | 16 ++++++--- .../omnichannel/agents/AgentInfoAction.tsx | 5 ++- .../agents/AgentsTable/RemoveAgentButton.tsx | 2 +- .../businessHours/BusinessHoursPage.js | 6 ++-- .../customFields/EditCustomFieldsPage.js | 5 ++- .../customFields/NewCustomFieldsPage.js | 5 ++- .../customFields/RemoveCustomFieldButton.tsx | 2 +- .../RemoveAgentButton.tsx | 2 +- .../DepartmentsTable/DepartmentItemMenu.tsx | 6 ++-- .../departments/EditDepartment.tsx | 4 +-- .../contextualBar/VoipInfoCallButton.tsx | 4 +-- .../directory/chats/contextualBar/ChatInfo.js | 6 ++-- .../chats/contextualBar/ChatInfoDirectory.js | 6 ++-- .../directory/contacts/ContactTable.tsx | 5 ++- .../contacts/contextualBar/ContactInfo.tsx | 10 +++--- .../omnichannel/installation/Installation.tsx | 1 + .../omnichannel/triggers/TriggersRow.tsx | 2 +- .../OutlookEventsList/OutlookEventsList.tsx | 7 ++-- .../Info/EditRoomInfo/EditChannel.js | 4 +-- .../contextualBar/RoomMembers/RoomMembers.tsx | 6 ++-- .../omnichannel/RemoveBusinessHourButton.js | 2 +- .../cannedResponses/CannedResponseEdit.tsx | 6 ++-- .../omnichannel/monitors/MonitorsTable.tsx | 2 +- .../slaPolicies/RemoveSlaButton.tsx | 2 +- .../ee/client/omnichannel/tags/TagEdit.js | 6 ++-- .../omnichannel/units/RemoveUnitButton.tsx | 2 +- .../ee/client/omnichannel/units/UnitEdit.js | 6 ++-- .../users/ContentForDays.tsx | 16 +++------ .../admin/users/ReachedSeatsCapModal.tsx | 5 ++- .../UserPageHeaderContentWithSeatsCap.tsx | 14 ++++---- .../rocketchat-i18n/i18n/en.i18n.json | 2 ++ .../ui-client/src/components/Card/index.ts | 3 ++ .../ui-video-conf/src/VideoConfButton.tsx | 5 ++- .../src/LoginServicesButton.tsx | 4 +-- yarn.lock | 34 +++++++++---------- 77 files changed, 232 insertions(+), 262 deletions(-) diff --git a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx index 17b555e0b820..e179c790f2dd 100644 --- a/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx +++ b/apps/meteor/client/components/InfoPanel/InfoPanelAction.tsx @@ -1,4 +1,4 @@ -import { Icon, Button } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { ComponentProps, ReactElement, ReactNode } from 'react'; import React from 'react'; @@ -14,8 +14,8 @@ const InfoPanelAction = ({ label, icon, ...props }: InfoPanelActionProps): React aria-label={typeof label === 'string' ? label : undefined} {...props} mi='x4' + icon={icon} > - {icon && <Icon name={icon} size='x20' mie='x4' />} {label} </Button> ); diff --git a/apps/meteor/client/components/TextCopy.tsx b/apps/meteor/client/components/TextCopy.tsx index 658cec401eba..64daea167213 100644 --- a/apps/meteor/client/components/TextCopy.tsx +++ b/apps/meteor/client/components/TextCopy.tsx @@ -1,4 +1,4 @@ -import { Box, Icon, Button, Scrollable } from '@rocket.chat/fuselage'; +import { Box, Button, Scrollable } from '@rocket.chat/fuselage'; import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; import React, { useCallback } from 'react'; @@ -14,6 +14,7 @@ type TextCopyProps = { wrapper?: (text: string) => ReactElement; } & ComponentProps<typeof Box>; +// TODO: useClipboard instead of navigator API. const TextCopy = ({ text, wrapper = defaultWrapperRenderer, ...props }: TextCopyProps): ReactElement => { const t = useTranslation(); const dispatchToastMessage = useToastMessageDispatch(); @@ -40,9 +41,7 @@ const TextCopy = ({ text, wrapper = defaultWrapperRenderer, ...props }: TextCopy {...props} > <Scrollable vertical>{wrapper(text)}</Scrollable> - <Button secondary square small flexShrink={0} onClick={onClick} title={t('Copy')}> - <Icon name='copy' size='x20' /> - </Button> + <Button icon='copy' secondary square small flexShrink={0} onClick={onClick} title={t('Copy')} /> </Box> ); }; diff --git a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx index 9ed7bb5c527a..c96a65bc85ef 100644 --- a/apps/meteor/client/components/UserInfo/UserInfoAction.tsx +++ b/apps/meteor/client/components/UserInfo/UserInfoAction.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactElement, ComponentProps } from 'react'; import React from 'react'; @@ -8,8 +8,7 @@ type UserInfoActionProps = { } & ComponentProps<typeof Button>; const UserInfoAction = ({ icon, label, ...props }: UserInfoActionProps): ReactElement => ( - <Button title={label} {...props} mi='x4'> - <Icon name={icon} size='x20' mie='x4' /> + <Button icon={icon} title={label} {...props} mi='x4'> {label} </Button> ); diff --git a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx index 8405677dc26a..925ee059d300 100644 --- a/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx +++ b/apps/meteor/client/components/avatar/RoomAvatarEditor.tsx @@ -1,7 +1,7 @@ import { isRoomFederated } from '@rocket.chat/core-typings'; import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; -import { Box, Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -62,20 +62,18 @@ const RoomAvatarEditor = ({ disabled = false, room, roomAvatar, onChangeAvatar } m='x12' > <ButtonGroup> - <Button disabled={isRoomFederated(room) || disabled} small title={t('Upload_user_avatar')} onClick={clickUpload}> - <Icon name='upload' size='x16' /> + <Button icon='upload' disabled={isRoomFederated(room) || disabled} small title={t('Upload_user_avatar')} onClick={clickUpload}> {t('Upload')} </Button> <Button small danger + icon='trash' title={t('Accounts_SetDefaultAvatar')} disabled={roomAvatar === null || isRoomFederated(room) || disabled} onClick={clickReset} - > - <Icon name='trash' size='x16' /> - </Button> + /> </ButtonGroup> </Box> </Box> diff --git a/apps/meteor/client/components/message/content/actions/MessageAction.tsx b/apps/meteor/client/components/message/content/actions/MessageAction.tsx index a98e605e6129..8cdf4d097e75 100644 --- a/apps/meteor/client/components/message/content/actions/MessageAction.tsx +++ b/apps/meteor/client/components/message/content/actions/MessageAction.tsx @@ -1,4 +1,4 @@ -import { Icon, Button } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useTranslation } from '@rocket.chat/ui-contexts'; @@ -28,8 +28,7 @@ const MessageAction = ({ icon, methodId, i18nLabel, label, runAction, danger }: const resolvedIcon = resolveLegacyIcon(icon); return ( - <Button data-method-id={methodId} onClick={runAction(methodId)} marginInline='x4' small danger={danger}> - {icon && <Icon name={resolvedIcon} />} + <Button icon={resolvedIcon} data-method-id={methodId} onClick={runAction(methodId)} marginInline='x4' small danger={danger}> {i18nLabel ? t(i18nLabel) : label} </Button> ); diff --git a/apps/meteor/client/sidebar/footer/voip/VoipFooter.tsx b/apps/meteor/client/sidebar/footer/voip/VoipFooter.tsx index 2eeeb525fd47..b317982b5708 100644 --- a/apps/meteor/client/sidebar/footer/voip/VoipFooter.tsx +++ b/apps/meteor/client/sidebar/footer/voip/VoipFooter.tsx @@ -1,7 +1,7 @@ import type { IVoipRoom, ICallerInfo, VoIpCallerInfo } from '@rocket.chat/core-typings'; import { VoipClientEvents } from '@rocket.chat/core-typings'; import { css } from '@rocket.chat/css-in-js'; -import { Box, Button, ButtonGroup, Icon, SidebarFooter, Menu, IconButton } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup, SidebarFooter, Menu, IconButton } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, MouseEvent, ReactNode } from 'react'; import React from 'react'; @@ -131,6 +131,7 @@ export const VoipFooter = ({ small square danger + icon='phone-off' disabled={paused} aria-label={t('End_call')} data-tooltip={t('End_Call')} @@ -140,29 +141,32 @@ export const VoipFooter = ({ paused && togglePause(false); return callActions.end(); }} - > - <Icon name='phone-off' size='x16' /> - </Button> + /> )} {callerState === 'OFFER_RECEIVED' && ( - <Button data-tooltip={t('Decline')} aria-label={t('Decline')} small square danger onClick={callActions.reject}> - <Icon name='phone-off' size='x16' /> - </Button> + <Button + icon='phone-off' + data-tooltip={t('Decline')} + aria-label={t('Decline')} + small + square + danger + onClick={callActions.reject} + /> )} {callerState === 'OFFER_RECEIVED' && ( <Button small square success + icon='phone' data-tooltip={t('Accept')} onClick={async (): Promise<void> => { callActions.pickUp(); const rid = await createRoom(caller); dispatchEvent({ event: VoipClientEvents['VOIP-CALL-STARTED'], rid }); }} - > - <Icon name='phone' size='x16' /> - </Button> + /> )} </ButtonGroup> </Box> diff --git a/apps/meteor/client/views/account/preferences/PreferencesMyDataSection.tsx b/apps/meteor/client/views/account/preferences/PreferencesMyDataSection.tsx index 2e3c3678e0f5..67a1084a4950 100644 --- a/apps/meteor/client/views/account/preferences/PreferencesMyDataSection.tsx +++ b/apps/meteor/client/views/account/preferences/PreferencesMyDataSection.tsx @@ -1,4 +1,4 @@ -import { Accordion, Field, FieldGroup, ButtonGroup, Button, Icon, Box } from '@rocket.chat/fuselage'; +import { Accordion, Field, FieldGroup, ButtonGroup, Button, Box } from '@rocket.chat/fuselage'; import { useSetModal, useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useCallback } from 'react'; @@ -84,12 +84,10 @@ const PreferencesMyDataSection = ({ ...props }): ReactElement => { <Field> <Field.Row> <ButtonGroup stretch flexGrow={1}> - <Button onClick={handleClickDownload}> - <Icon name='download' size={20} /> + <Button icon='download' onClick={handleClickDownload}> {t('Download_My_Data')} </Button> - <Button onClick={handleClickExport}> - <Icon name='download' size={20} /> + <Button icon='download' onClick={handleClickExport}> {t('Export_My_Data')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/account/profile/AccountProfilePage.tsx b/apps/meteor/client/views/account/profile/AccountProfilePage.tsx index b2f629dd38fa..abca6a407c6e 100644 --- a/apps/meteor/client/views/account/profile/AccountProfilePage.tsx +++ b/apps/meteor/client/views/account/profile/AccountProfilePage.tsx @@ -1,5 +1,5 @@ import type { AvatarObject, IUser } from '@rocket.chat/core-typings'; -import { ButtonGroup, Button, Box, Icon } from '@rocket.chat/fuselage'; +import { ButtonGroup, Button, Box } from '@rocket.chat/fuselage'; import { SHA256 } from '@rocket.chat/sha256'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { @@ -264,8 +264,7 @@ const AccountProfilePage = (): ReactElement => { {t('Logout_Others')} </Button> {allowDeleteOwnAccount && ( - <Button danger onClick={handleDeleteOwnAccount}> - <Icon name='trash' size='x20' mie='x4' /> + <Button icon='trash' danger onClick={handleDeleteOwnAccount}> {t('Delete_my_account')} </Button> )} diff --git a/apps/meteor/client/views/admin/cloud/CopyStep.tsx b/apps/meteor/client/views/admin/cloud/CopyStep.tsx index 4232572ea7bf..7476b9dbe514 100644 --- a/apps/meteor/client/views/admin/cloud/CopyStep.tsx +++ b/apps/meteor/client/views/admin/cloud/CopyStep.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Icon, Scrollable, Modal } from '@rocket.chat/fuselage'; +import { Box, Button, Scrollable, Modal } from '@rocket.chat/fuselage'; import { useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import Clipboard from 'clipboard'; import type { FC } from 'react'; @@ -57,9 +57,7 @@ const CopyStep: FC<CopyStepProps> = ({ onNextButtonClick }) => { {clientKey} </Box> </Scrollable> - <Button ref={copyRef} primary data-clipboard-text={clientKey}> - <Icon name='copy' /> {t('Copy')} - </Button> + <Button icon='copy' ref={copyRef} primary data-clipboard-text={clientKey} /> </Box> <MarkdownText preserveHtml={true} content={t('Cloud_click_here', { CLOUD_CONSOLE_URL })} /> </Modal.Content> diff --git a/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx b/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx index a3de1c5384c8..d5b0ec7cf771 100644 --- a/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx +++ b/apps/meteor/client/views/admin/cloud/components/RegisterWorkspaceMenu.tsx @@ -1,4 +1,4 @@ -import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; @@ -35,12 +35,10 @@ const RegisterWorkspaceMenu = ({ <ButtonGroup> {isWorkspaceRegistered && isConnectedToCloud && ( <> - <Button role='link' onClick={() => handleLinkClick(CLOUD_CONSOLE_URL)}> - <Icon name='new-window' size='x20' pie={4} /> + <Button icon='new-window' role='link' onClick={() => handleLinkClick(CLOUD_CONSOLE_URL)}> {t('Cloud')} </Button> - <Button onClick={handleManageButton}> - <Icon name='customize' size='x20' pie={4} /> + <Button icon='customize' onClick={handleManageButton}> {t('Manage')} </Button> </> diff --git a/apps/meteor/client/views/admin/cloud/modals/RegisteredWorkspaceModal.tsx b/apps/meteor/client/views/admin/cloud/modals/RegisteredWorkspaceModal.tsx index 98d4ea0ab845..1a77893548f2 100644 --- a/apps/meteor/client/views/admin/cloud/modals/RegisteredWorkspaceModal.tsx +++ b/apps/meteor/client/views/admin/cloud/modals/RegisteredWorkspaceModal.tsx @@ -1,4 +1,4 @@ -import { Box, Button, ButtonGroup, Icon, Modal } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup, Modal } from '@rocket.chat/fuselage'; import { useSafely } from '@rocket.chat/fuselage-hooks'; import { useMethod, useSetModal, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useState } from 'react'; @@ -73,8 +73,7 @@ const RegisteredWorkspaceModal = ({ onClose, onStatusChange, ...props }: Registe <Button secondary danger onClick={handleDisconnect}> {t('Disconnect')} </Button> - <Button onClick={handleSyncAction} disabled={isSyncing}> - <Icon pie={4} name='reload' size='x20' /> + <Button icon='reload' onClick={handleSyncAction} disabled={isSyncing}> {t('Sync')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx index 6d12c25f5239..9c89e7556f6a 100644 --- a/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx @@ -94,6 +94,7 @@ const AddCustomEmoji = ({ close, onChange, ...props }: AddCustomEmojiProps): Rea <Field> <Field.Label alignSelf='stretch' display='flex' justifyContent='space-between' alignItems='center'> {t('Custom_Emoji')} + {/* FIXME: replace to IconButton */} <Button square onClick={clickUpload}> <Icon name='upload' size='x20' /> </Button> diff --git a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx index 188802bb7c87..3fa36c6ad33f 100644 --- a/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx +++ b/apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx @@ -1,4 +1,4 @@ -import { Box, Button, ButtonGroup, Margins, TextInput, Field, Icon, FieldGroup, IconButton } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup, Margins, TextInput, Field, FieldGroup, IconButton } from '@rocket.chat/fuselage'; import { useSetModal, useToastMessageDispatch, useAbsoluteUrl, useTranslation } from '@rocket.chat/ui-contexts'; import type { FC, ChangeEvent } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react'; @@ -170,8 +170,7 @@ const EditCustomEmoji: FC<EditCustomEmojiProps> = ({ close, onChange, data, ...p </Button> </ButtonGroup> <ButtonGroup mbs='x8' stretch> - <Button danger onClick={handleDeleteButtonClick}> - <Icon name='trash' mie='x4' /> + <Button icon='trash' danger onClick={handleDeleteButtonClick}> {t('Delete')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx index e69186ed5521..2cdb0cf7e0c7 100644 --- a/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/AddCustomSound.tsx @@ -99,6 +99,7 @@ const AddCustomSound = ({ goToNew, close, onChange, ...props }: AddCustomSoundPr <Field.Label alignSelf='stretch'>{t('Sound_File_mp3')}</Field.Label> <Box display='flex' flexDirection='row' mbs='none'> <Margins inline='x4'> + {/* FIXME: replace to IconButton */} <Button square onClick={clickUpload}> <Icon name='upload' size='x20' /> </Button> diff --git a/apps/meteor/client/views/admin/customSounds/EditSound.tsx b/apps/meteor/client/views/admin/customSounds/EditSound.tsx index 19e3958c74a0..05d9c7ab4ca1 100644 --- a/apps/meteor/client/views/admin/customSounds/EditSound.tsx +++ b/apps/meteor/client/views/admin/customSounds/EditSound.tsx @@ -1,4 +1,4 @@ -import { Box, Button, ButtonGroup, Margins, TextInput, Field, Icon, IconButton } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup, Margins, TextInput, Field, IconButton } from '@rocket.chat/fuselage'; import { useSetModal, useToastMessageDispatch, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, SyntheticEvent } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react'; @@ -147,8 +147,7 @@ function EditSound({ close, onChange, data, ...props }: EditSoundProps): ReactEl </Button> </ButtonGroup> <ButtonGroup mbs='x8' stretch> - <Button danger onClick={handleDeleteButtonClick}> - <Icon name='trash' mie='x4' /> + <Button icon='trash' danger onClick={handleDeleteButtonClick}> {t('Delete')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx index f85172533ee5..4ea8bd67af0e 100644 --- a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx +++ b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusForm.tsx @@ -1,6 +1,6 @@ import type { IUserStatus } from '@rocket.chat/core-typings'; import type { SelectOption } from '@rocket.chat/fuselage'; -import { FieldGroup, Button, ButtonGroup, TextInput, Field, Select, Icon } from '@rocket.chat/fuselage'; +import { FieldGroup, Button, ButtonGroup, TextInput, Field, Select } from '@rocket.chat/fuselage'; import { useUniqueId } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useRoute, useToastMessageDispatch, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -121,8 +121,7 @@ const CustomUserStatusForm = ({ onClose, onReload, status }: CustomUserStatusFor </ButtonGroup> {_id && ( <ButtonGroup mbs='x8' stretch> - <Button danger onClick={handleDeleteStatus}> - <Icon name='trash' mie='x4' /> + <Button icon='trash' danger onClick={handleDeleteStatus}> {t('Delete')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusService.tsx b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusService.tsx index fbcfb1118308..ee457b72957e 100644 --- a/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusService.tsx +++ b/apps/meteor/client/views/admin/customUserStatus/CustomUserStatusService.tsx @@ -3,7 +3,6 @@ import { Button, ButtonGroup, Callout, - Icon, Margins, ProgressBar, Skeleton, @@ -43,9 +42,9 @@ const CustomUserStatusService = () => { return ( <Box display='flex' flexDirection='column' alignItems='center' pb='x20' color='default'> <StatesIcon name='circle-exclamation' /> - <StatesSubtitle>Unable to load active connections</StatesSubtitle> - <StatesAction onClick={() => result.refetch()}> - <Icon name='reload' /> Retry + <StatesSubtitle>{t('Unable_to_load_active_connections')}</StatesSubtitle> + <StatesAction icon='reload' onClick={() => result.refetch()}> + {t('Retry')} </StatesAction> </Box> ); diff --git a/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx b/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx index b2e93594fbc4..f58f27a196da 100644 --- a/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx +++ b/apps/meteor/client/views/admin/emailInbox/EmailInboxPage.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import { useRoute, useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -19,8 +19,7 @@ const EmailInboxPage = (): ReactElement => { <Page> <Page.Header title={t('Email_Inboxes')}> {context && ( - <Button onClick={(): void => emailInboxRoute.push({})}> - <Icon name='back' /> + <Button icon='back' onClick={(): void => emailInboxRoute.push({})}> {t('Back')} </Button> )} diff --git a/apps/meteor/client/views/admin/emailInbox/SendTestButton.tsx b/apps/meteor/client/views/admin/emailInbox/SendTestButton.tsx index ed44621137bf..c844b6e6f56e 100644 --- a/apps/meteor/client/views/admin/emailInbox/SendTestButton.tsx +++ b/apps/meteor/client/views/admin/emailInbox/SendTestButton.tsx @@ -1,5 +1,5 @@ import type { IEmailInboxPayload } from '@rocket.chat/core-typings'; -import { Box, Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import { useToastMessageDispatch, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -31,11 +31,8 @@ const SendTestButton = ({ id }: { id: IEmailInboxPayload['_id'] }): ReactElement return ( <GenericTableCell withTruncatedText> - <Button small onClick={handleOnClick}> - <Box display='flex' alignItems='center'> - <Icon mie='x4' size='x16' name='send' /> - {t('Send_Test_Email')} - </Box> + <Button icon='send' small onClick={handleOnClick}> + {t('Send_Test_Email')} </Button> </GenericTableCell> ); diff --git a/apps/meteor/client/views/admin/import/NewImportPage.js b/apps/meteor/client/views/admin/import/NewImportPage.js index b616579eb920..92d290c20fd2 100644 --- a/apps/meteor/client/views/admin/import/NewImportPage.js +++ b/apps/meteor/client/views/admin/import/NewImportPage.js @@ -5,7 +5,6 @@ import { Callout, Chip, Field, - Icon, Margins, Select, InputBox, @@ -168,8 +167,8 @@ function NewImportPage() { <Page className='page-settings'> <Page.Header title={t('Import_New_File')}> <ButtonGroup> - <Button secondary onClick={handleBackToImportsButtonClick}> - <Icon name='back' /> {t('Back_to_imports')} + <Button icon='back' secondary onClick={handleBackToImportsButtonClick}> + {t('Back_to_imports')} </Button> {importer && ( <Button primary minHeight='x40' disabled={isLoading} onClick={handleImportButtonClick}> diff --git a/apps/meteor/client/views/admin/import/PrepareImportPage.js b/apps/meteor/client/views/admin/import/PrepareImportPage.js index 59e05b5f85d0..889646cf8986 100644 --- a/apps/meteor/client/views/admin/import/PrepareImportPage.js +++ b/apps/meteor/client/views/admin/import/PrepareImportPage.js @@ -1,4 +1,4 @@ -import { Badge, Box, Button, ButtonGroup, Icon, Margins, Throbber, Tabs } from '@rocket.chat/fuselage'; +import { Badge, Box, Button, ButtonGroup, Margins, Throbber, Tabs } from '@rocket.chat/fuselage'; import { useDebouncedValue, useSafely } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useTranslation, useStream, useRouter } from '@rocket.chat/ui-contexts'; import React, { useEffect, useState, useMemo } from 'react'; @@ -171,8 +171,8 @@ function PrepareImportPage() { <Page> <Page.Header title={t('Importing_Data')}> <ButtonGroup> - <Button secondary onClick={handleBackToImportsButtonClick}> - <Icon name='back' /> {t('Back_to_imports')} + <Button icon='back' secondary onClick={handleBackToImportsButtonClick}> + {t('Back_to_imports')} </Button> <Button primary disabled={isImporting || handleMinimumImportData} onClick={handleStartButtonClick}> {t('Importer_Prepare_Start_Import')} diff --git a/apps/meteor/client/views/admin/info/InformationRoute.tsx b/apps/meteor/client/views/admin/info/InformationRoute.tsx index 97b7faa79796..8bc7bf023c0c 100644 --- a/apps/meteor/client/views/admin/info/InformationRoute.tsx +++ b/apps/meteor/client/views/admin/info/InformationRoute.tsx @@ -1,5 +1,5 @@ import type { IStats, Serialized } from '@rocket.chat/core-typings'; -import { Callout, ButtonGroup, Button, Icon } from '@rocket.chat/fuselage'; +import { Callout, ButtonGroup, Button } from '@rocket.chat/fuselage'; import type { IInstance } from '@rocket.chat/rest-typings'; import { usePermission, useServerInformation, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -85,8 +85,8 @@ const InformationRoute = (): ReactElement => { <Page> <Page.Header title={t('Workspace')}> <ButtonGroup> - <Button primary type='button' onClick={handleClickRefreshButton}> - <Icon name='reload' /> {t('Refresh')} + <Button icon='reload' primary type='button' onClick={handleClickRefreshButton}> + {t('Refresh')} </Button> </ButtonGroup> </Page.Header> diff --git a/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx b/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx index b0970ec2408b..b8d9df52372f 100644 --- a/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx +++ b/apps/meteor/client/views/admin/info/OfflineLicenseModal.tsx @@ -1,4 +1,4 @@ -import { Modal, Box, ButtonGroup, Button, Scrollable, Callout, Margins, Icon } from '@rocket.chat/fuselage'; +import { Modal, Box, ButtonGroup, Button, Scrollable, Callout, Margins } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useEndpoint, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts'; import type { ComponentProps, FormEvent, ReactElement } from 'react'; @@ -99,8 +99,7 @@ const OfflineLicenseModal = ({ onClose, license, licenseStatus, ...props }: Offl /> </Scrollable> <ButtonGroup align='start'> - <Button primary small disabled={isUpdating} onClick={handlePaste}> - <Icon name='clipboard' /> + <Button icon='clipboard' primary small disabled={isUpdating} onClick={handlePaste}> {t('Paste')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js b/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js index ce3d0ece244e..1a4e246d09cf 100644 --- a/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js +++ b/apps/meteor/client/views/admin/integrations/edit/EditIntegrationsPage.js @@ -1,4 +1,4 @@ -import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; @@ -26,8 +26,8 @@ function EditIntegrationsPage({ ...props }) { <Page flexDirection='column' {...props}> <Page.Header title={type === 'incoming' ? t('Integration_Incoming_WebHook') : t('Integration_Outgoing_WebHook')}> <ButtonGroup> - <Button onClick={handleClickReturn}> - <Icon name='back' size='x16' /> {t('Back')} + <Button icon='back' onClick={handleClickReturn}> + {t('Back')} </Button> {type === 'outgoing' && <Button onClick={handleClickHistory}>{t('History')}</Button>} </ButtonGroup> diff --git a/apps/meteor/client/views/admin/integrations/edit/OutgoingWebhookHistoryPage.tsx b/apps/meteor/client/views/admin/integrations/edit/OutgoingWebhookHistoryPage.tsx index 8cbf9c08a751..1c911fd307c8 100644 --- a/apps/meteor/client/views/admin/integrations/edit/OutgoingWebhookHistoryPage.tsx +++ b/apps/meteor/client/views/admin/integrations/edit/OutgoingWebhookHistoryPage.tsx @@ -1,4 +1,4 @@ -import { Button, ButtonGroup, Icon, Pagination } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup, Pagination } from '@rocket.chat/fuselage'; import { useToastMessageDispatch, useRoute, useRouteParameter, useMethod, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import type { ReactElement, ComponentProps } from 'react'; @@ -114,11 +114,11 @@ function OutgoingWebhookHistoryPage(props: ComponentProps<typeof Page>): ReactEl <Page flexDirection='column' {...props}> <Page.Header title={t('Integration_Outgoing_WebHook_History')}> <ButtonGroup> - <Button onClick={handleClickReturn}> - <Icon name='back' size='x16' /> {t('Back')} + <Button icon='back' onClick={handleClickReturn}> + {t('Back')} </Button> - <Button danger onClick={handleClearHistory} disabled={total === 0}> - <Icon name='trash' /> {t('clear_history')} + <Button icon='trash' danger onClick={handleClearHistory} disabled={total === 0}> + {t('clear_history')} </Button> </ButtonGroup> </Page.Header> diff --git a/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js b/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js index a5456ee9f255..1279cf29cb74 100644 --- a/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js +++ b/apps/meteor/client/views/admin/integrations/new/NewIntegrationsPage.js @@ -1,4 +1,4 @@ -import { Tabs, Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Tabs, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback } from 'react'; @@ -28,8 +28,8 @@ export default function NewIntegrationsPage({ ...props }) { <Page flexDirection='column' {...props}> <Page.Header title={t('Integrations')}> <ButtonGroup> - <Button onClick={handleClickReturn}> - <Icon name='back' size='x16' /> {t('Back')} + <Button icon='back' onClick={handleClickReturn}> + {t('Back')} </Button> </ButtonGroup> </Page.Header> diff --git a/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx b/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx index 8b2e31763fc7..0791da8d6c39 100644 --- a/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx +++ b/apps/meteor/client/views/admin/moderation/MessageContextFooter.tsx @@ -1,4 +1,4 @@ -import { Button, Icon, Menu, Option, ButtonGroup } from '@rocket.chat/fuselage'; +import { Button, Menu, Option, ButtonGroup } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import React from 'react'; import type { FC } from 'react'; @@ -14,8 +14,15 @@ const MessageContextFooter: FC<{ userId: string }> = ({ userId }) => { return ( <ButtonGroup flexGrow={1}> - <Button flexGrow={1} onClick={action} title={t('delete-message')} aria-label={t('Moderation_Delete_all_messages')} danger> - <Icon name='trash' /> {t('Moderation_Delete_all_messages')} + <Button + icon='trash' + flexGrow={1} + onClick={action} + title={t('delete-message')} + aria-label={t('Moderation_Delete_all_messages')} + danger + > + {t('Moderation_Delete_all_messages')} </Button> <Menu diff --git a/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx b/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx index c0a22ddd71ec..a6eeea270eb6 100644 --- a/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx +++ b/apps/meteor/client/views/admin/oauthApps/EditOauthApp.tsx @@ -1,5 +1,5 @@ import type { IOAuthApps, Serialized } from '@rocket.chat/core-typings'; -import { Button, ButtonGroup, TextInput, Field, Icon, TextAreaInput, ToggleSwitch, FieldGroup } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup, TextInput, Field, TextAreaInput, ToggleSwitch, FieldGroup } from '@rocket.chat/fuselage'; import { useSetModal, useToastMessageDispatch, useRoute, useAbsoluteUrl, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts'; import type { ReactElement, ComponentProps } from 'react'; import React, { useCallback, useMemo } from 'react'; @@ -152,8 +152,7 @@ const EditOauthApp = ({ onChange, data, ...props }: EditOauthAppProps): ReactEle <Field> <Field.Row> <ButtonGroup stretch w='full'> - <Button danger onClick={openConfirmDelete}> - <Icon name='trash' mie='x4' /> + <Button icon='trash' danger onClick={openConfirmDelete}> {t('Delete')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx b/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx index 36648f168614..0fde33b4a7b7 100644 --- a/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx +++ b/apps/meteor/client/views/admin/oauthApps/OAuthAppsPage.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -21,8 +21,7 @@ const OAuthAppsPage = (): ReactElement => { <Page> <Page.Header title={t('Third_party_login')}> {context && ( - <Button alignSelf='flex-end' onClick={(): void => router.push({})}> - <Icon name='back' /> + <Button icon='back' alignSelf='flex-end' onClick={(): void => router.push({})}> {t('Back')} </Button> )} diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx index f1b637c1e0d4..bea2cc5ad1c0 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx @@ -1,5 +1,5 @@ import type { IRole } from '@rocket.chat/core-typings'; -import { Margins, Icon, Button } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -25,11 +25,8 @@ const RoleHeader = ({ _id, name, description }: RoleHeaderProps): ReactElement = return ( <GenericTableHeaderCell pi='x4' p='x8'> - <Button secondary onClick={handleEditRole}> - <Margins inline='x2'> - <span>{description || name}</span> - <Icon name='edit' size='x16' /> - </Margins> + <Button icon='edit' secondary onClick={handleEditRole}> + {description || name} </Button> </GenericTableHeaderCell> ); diff --git a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx index b4a9dcebb882..a754d1136529 100644 --- a/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx +++ b/apps/meteor/client/views/admin/permissions/UsersInRole/UsersInRoleTable/UsersInRoleTableRow.tsx @@ -43,6 +43,7 @@ const UsersInRoleTableRow = ({ user, onRemove }: UsersInRoleTableRowProps): Reac </GenericTableCell> <GenericTableCell withTruncatedText>{email}</GenericTableCell> <GenericTableCell withTruncatedText> + {/* FIXME: Replace to IconButton */} <Button small square secondary danger onClick={handleRemove}> <Icon name='trash' size='x20' /> </Button> diff --git a/apps/meteor/client/views/admin/rooms/EditRoom.tsx b/apps/meteor/client/views/admin/rooms/EditRoom.tsx index 8f91054ebdf7..1dbc3bf0356f 100644 --- a/apps/meteor/client/views/admin/rooms/EditRoom.tsx +++ b/apps/meteor/client/views/admin/rooms/EditRoom.tsx @@ -1,6 +1,6 @@ import type { IRoom, RoomAdminFieldsType } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; -import { Box, Button, ButtonGroup, TextInput, Field, ToggleSwitch, Icon, TextAreaInput } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup, TextInput, Field, ToggleSwitch, TextAreaInput } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSetModal, useToastMessageDispatch, useRoute, usePermission, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -347,8 +347,7 @@ const EditRoom = ({ room, onChange, onDelete }: EditRoomProps): ReactElement => </Button> </ButtonGroup> <ButtonGroup mbs='x8' stretch> - <Button danger disabled={deleting || !canDelete || isRoomFederated(room)} onClick={handleDelete}> - <Icon name='trash' size='x16' /> + <Button icon='trash' danger disabled={deleting || !canDelete || isRoomFederated(room)} onClick={handleDelete}> {t('Delete')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx b/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx index d021496567e3..5871296cee6a 100644 --- a/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx +++ b/apps/meteor/client/views/admin/settings/inputs/AssetSettingInput.tsx @@ -72,13 +72,12 @@ function AssetSettingInput({ _id, label, value, asset, fileConstraints }: AssetS /> ) : ( <div className='preview no-file background-transparent-light secondary-font-color'> - <Icon name='upload' /> + <Icon size='x16' name='upload' /> </div> )} <div className='action'> {value?.url ? ( - <Button onClick={handleDeleteButtonClick}> - <Icon name='trash' /> + <Button icon='trash' onClick={handleDeleteButtonClick}> {t('Delete')} </Button> ) : ( diff --git a/apps/meteor/client/views/admin/upgrade/UpgradePageError/UpgradePageError.tsx b/apps/meteor/client/views/admin/upgrade/UpgradePageError/UpgradePageError.tsx index d74799890b6d..ecc9925eb619 100644 --- a/apps/meteor/client/views/admin/upgrade/UpgradePageError/UpgradePageError.tsx +++ b/apps/meteor/client/views/admin/upgrade/UpgradePageError/UpgradePageError.tsx @@ -1,4 +1,4 @@ -import { States, StatesIcon, StatesSubtitle, StatesTitle, StatesActions, Button, Icon, Box } from '@rocket.chat/fuselage'; +import { States, StatesIcon, StatesSubtitle, StatesTitle, StatesActions, StatesAction, Box } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -17,9 +17,9 @@ const UpgradePageError = (): ReactElement => { <strong>{t('Upgrade_tab_connection_error_restore')}</strong> </StatesSubtitle> <StatesActions> - <Button onClick={handleReconnect}> - <Icon name='reload' /> {t('Refresh')} - </Button> + <StatesAction icon='reload' onClick={handleReconnect}> + {t('Refresh')} + </StatesAction> </StatesActions> </States> </Box> diff --git a/apps/meteor/client/views/admin/users/InviteUsers.tsx b/apps/meteor/client/views/admin/users/InviteUsers.tsx index cad5e8c93041..e1bb865b4a33 100644 --- a/apps/meteor/client/views/admin/users/InviteUsers.tsx +++ b/apps/meteor/client/views/admin/users/InviteUsers.tsx @@ -2,7 +2,6 @@ import { Box, Button, ButtonGroup, - Icon, States, StatesAction, StatesActions, @@ -62,8 +61,8 @@ const InviteUsers = (props: InviteUsersProps): ReactElement => { </ContextualbarScrollableContent> <ContextualbarFooter> <ButtonGroup stretch> - <Button primary onClick={handleClick} disabled={!getEmails(text).length} alignItems='stretch' mb='x8'> - <Icon name='send' size='x16' /> {t('Send')} + <Button icon='send' primary onClick={handleClick} disabled={!getEmails(text).length} alignItems='stretch' mb='x8'> + {t('Send')} </Button> </ButtonGroup> </ContextualbarFooter> diff --git a/apps/meteor/client/views/admin/users/UsersPage.tsx b/apps/meteor/client/views/admin/users/UsersPage.tsx index 2eacbd32dc47..da18df433c3b 100644 --- a/apps/meteor/client/views/admin/users/UsersPage.tsx +++ b/apps/meteor/client/views/admin/users/UsersPage.tsx @@ -1,4 +1,4 @@ -import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; import { usePermission, useRoute, useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useEffect, useRef } from 'react'; @@ -60,13 +60,13 @@ const UsersPage = (): ReactElement => { ) : ( <ButtonGroup> {canCreateUser && ( - <Button onClick={handleNewUser}> - <Icon size='x20' name='user-plus' /> {t('New')} + <Button icon='user-plus' onClick={handleNewUser}> + {t('New')} </Button> )} {canBulkCreateUser && ( - <Button onClick={handleInviteUser}> - <Icon size='x20' name='mail' /> {t('Invite')} + <Button icon='mail' onClick={handleInviteUser}> + {t('Invite')} </Button> )} </ButtonGroup> diff --git a/apps/meteor/client/views/home/HomePageHeader.tsx b/apps/meteor/client/views/home/HomePageHeader.tsx index 498e3839b032..cfbcd471409d 100644 --- a/apps/meteor/client/views/home/HomePageHeader.tsx +++ b/apps/meteor/client/views/home/HomePageHeader.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import { useSetting, useTranslation, useAllPermissions, useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -16,8 +16,8 @@ const HomepageHeader = (): ReactElement => { return ( <PageHeader title={title} data-qa-id='home-header' role='heading'> {canEditLayout && ( - <Button onClick={() => settingsRoute.push({ group: 'Layout' })}> - <Icon name='pencil' size='x16' /> {t('Customize')} + <Button icon='pencil' onClick={() => settingsRoute.push({ group: 'Layout' })}> + {t('Customize')} </Button> )} </PageHeader> diff --git a/apps/meteor/client/views/home/cards/CustomContentCard.tsx b/apps/meteor/client/views/home/cards/CustomContentCard.tsx index 8f59c00486b1..57ebf4a09bac 100644 --- a/apps/meteor/client/views/home/cards/CustomContentCard.tsx +++ b/apps/meteor/client/views/home/cards/CustomContentCard.tsx @@ -69,21 +69,22 @@ const CustomContentCard = (): ReactElement | null => { {t('Customize_Content')} </Button> <Button + icon={willNotShowCustomContent ? 'eye' : 'eye-off'} disabled={isCustomContentBodyEmpty || (isCustomContentVisible && isCustomContentOnly)} title={isCustomContentBodyEmpty ? t('Action_Available_After_Custom_Content_Added') : userVisibilityTooltipText} onClick={handleChangeCustomContentVisibility} role='button' > - <Icon mie='x4' name={willNotShowCustomContent ? 'eye' : 'eye-off'} size='x16' /> {willNotShowCustomContent ? t('Show_To_Workspace') : t('Hide_On_Workspace')} </Button> <Button + icon='lightning' disabled={willNotShowCustomContent || !isEnterprise} title={!isEnterprise ? t('Enterprise_Only') : customContentOnlyTooltipText} onClick={handleOnlyShowCustomContent} role='button' > - <Icon name='lightning' size='x16' /> {!isCustomContentOnly ? t('Show_Only_This_Content') : t('Show_default_content')} + {!isCustomContentOnly ? t('Show_Only_This_Content') : t('Show_default_content')} </Button> </Card.Footer> </Card.FooterWrapper> diff --git a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx index a1e31e7023fc..8d9c961886f8 100644 --- a/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx +++ b/apps/meteor/client/views/marketplace/AppDetailsPage/tabs/AppStatus/AppStatus.tsx @@ -1,5 +1,5 @@ import type { App } from '@rocket.chat/core-typings'; -import { Box, Button, Icon, Throbber, Tag, Margins } from '@rocket.chat/fuselage'; +import { Box, Button, Throbber, Tag, Margins } from '@rocket.chat/fuselage'; import { useSafely } from '@rocket.chat/fuselage-hooks'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; import { useRouteParameter, usePermission, useSetModal, useTranslation } from '@rocket.chat/ui-contexts'; @@ -128,20 +128,15 @@ const AppStatus = ({ app, showStatus = true, isAppDetailsPage, installed, ...pro invisible={!showStatus && !loading} > <Button + icon={!loading && button.icon ? button.icon : undefined} primary small disabled={loading || (action === 'request' && (app?.requestedEndUser || endUserRequested))} onClick={handleAcquireApp} mie='x8' > - {loading ? ( - <Throbber inheritColor /> - ) : ( - <> - {button.icon && <Icon name={button.icon} size='x16' mie='x4' />} - {t(button.label.replace(' ', '_') as TranslationKey)} - </> - )} + {loading && <Throbber inheritColor />} + {!loading && t(button.label.replace(' ', '_') as TranslationKey)} </Button> {shouldShowPriceDisplay && !installed && ( diff --git a/apps/meteor/client/views/marketplace/AppInstallPage.js b/apps/meteor/client/views/marketplace/AppInstallPage.js index 860a09d6ab75..8f388199a820 100644 --- a/apps/meteor/client/views/marketplace/AppInstallPage.js +++ b/apps/meteor/client/views/marketplace/AppInstallPage.js @@ -216,8 +216,7 @@ function AppInstallPage() { <TextInput value={file.name || ''} addon={ - <Button small primary onClick={handleUploadButtonClick} mb='neg-x4' mie='neg-x8'> - <Icon name='upload' size='x12' /> + <Button icon='upload' small primary onClick={handleUploadButtonClick} mb='neg-x4' mie='neg-x8'> {t('Browse_Files')} </Button> } diff --git a/apps/meteor/client/views/marketplace/AppMenu.js b/apps/meteor/client/views/marketplace/AppMenu.js index 6f90781e6101..d6c2fe0c9875 100644 --- a/apps/meteor/client/views/marketplace/AppMenu.js +++ b/apps/meteor/client/views/marketplace/AppMenu.js @@ -280,7 +280,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { subscribe: { label: ( <> - <Icon name={incompatibleIconName(app, 'subscribe')} size='x16' marginInlineEnd='x4' /> + <Icon name={incompatibleIconName(app, 'subscribe')} size='x16' mie='x4' /> {t('Subscription')} </> ), @@ -294,7 +294,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { acquire: { label: ( <> - {isAdminUser && <Icon name={incompatibleIconName(app, 'install')} size='x16' marginInlineEnd='x4' />} + {isAdminUser && <Icon name={incompatibleIconName(app, 'install')} size='x16' mie='x4' />} {t(button.label.replace(' ', '_'))} </> ), @@ -311,7 +311,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { viewLogs: { label: ( <> - <Icon name='desktop-text' size='x16' marginInlineEnd='x4' /> + <Icon name='desktop-text' size='x16' mie='x4' /> {t('View_Logs')} </> ), @@ -324,7 +324,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { update: { label: ( <> - <Icon name={incompatibleIconName(app, 'update')} size='x16' marginInlineEnd='x4' /> + <Icon name={incompatibleIconName(app, 'update')} size='x16' mie='x4' /> {t('Update')} </> ), @@ -337,7 +337,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { disable: { label: ( <Box color='status-font-on-warning'> - <Icon name='ban' size='x16' marginInlineEnd='x4' /> + <Icon name='ban' size='x16' mie='x4' /> {t('Disable')} </Box> ), @@ -350,7 +350,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { enable: { label: ( <> - <Icon name='check' size='x16' marginInlineEnd='x4' /> + <Icon name='check' size='x16' mie='x4' /> {t('Enable')} </> ), @@ -369,7 +369,7 @@ function AppMenu({ app, isAppDetailsPage, ...props }) { uninstall: { label: ( <Box color='status-font-on-danger'> - <Icon name='trash' size='x16' marginInlineEnd='x4' /> + <Icon name='trash' size='x16' mie='x4' /> {t('Uninstall')} </Box> ), diff --git a/apps/meteor/client/views/marketplace/AppsPage/AppsPageConnectionError.tsx b/apps/meteor/client/views/marketplace/AppsPage/AppsPageConnectionError.tsx index c6edec425745..d45585c4f9b2 100644 --- a/apps/meteor/client/views/marketplace/AppsPage/AppsPageConnectionError.tsx +++ b/apps/meteor/client/views/marketplace/AppsPage/AppsPageConnectionError.tsx @@ -1,4 +1,4 @@ -import { Box, States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction, Icon } from '@rocket.chat/fuselage'; +import { Box, States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; @@ -13,8 +13,7 @@ const AppsPageContentError = ({ onButtonClick }: { onButtonClick: () => void }): <StatesTitle>{t('Connection_error')}</StatesTitle> <StatesSubtitle>{t('Marketplace_error')}</StatesSubtitle> <StatesActions> - <StatesAction onClick={onButtonClick}> - <Icon mie='x4' size='x20' name='reload' /> + <StatesAction icon='reload' onClick={onButtonClick}> {t('Reload_page')} </StatesAction> </StatesActions> diff --git a/apps/meteor/client/views/meet/MeetPage.tsx b/apps/meteor/client/views/meet/MeetPage.tsx index b5378b94dd78..ccca3c02a773 100644 --- a/apps/meteor/client/views/meet/MeetPage.tsx +++ b/apps/meteor/client/views/meet/MeetPage.tsx @@ -1,5 +1,5 @@ -import { Button, Box, Icon, Flex } from '@rocket.chat/fuselage'; -import { useRouteParameter, useSearchParameter } from '@rocket.chat/ui-contexts'; +import { Button, Box, Flex } from '@rocket.chat/fuselage'; +import { useRouteParameter, useSearchParameter, useTranslation } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; import React, { useEffect, useState, useCallback } from 'react'; @@ -11,6 +11,7 @@ import CallPage from './CallPage'; import './styles.css'; const MeetPage = () => { + const t = useTranslation(); const [isRoomMember, setIsRoomMember] = useState(false); const [status, setStatus] = useState(null); const [visitorId, setVisitorId] = useState(null); @@ -129,9 +130,14 @@ const MeetPage = () => { </p> </Box> <Box position='absolute' alignItems='center' style={{ bottom: '20%' }}> - <Button square title='Close Window' onClick={closeCallTab} backgroundColor='dark' borderColor='extra-dark'> - <Icon name='cross' size='x16' color='white' /> - </Button> + <Button + icon='cross' + square + title={t('Close_Window')} + onClick={closeCallTab} + backgroundColor='dark' + borderColor='extra-dark' + ></Button> </Box> </Box> </Flex.Container> diff --git a/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx b/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx index 9cb6e51170b7..6618f120888c 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentInfoAction.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { FC, HtmlHTMLAttributes } from 'react'; import React from 'react'; @@ -10,8 +10,7 @@ type AgentInfoActionProps = { } & Omit<HtmlHTMLAttributes<HTMLElement>, 'is'>; const AgentInfoAction: FC<AgentInfoActionProps> = ({ icon, label, ...props }) => ( - <Button data-qa={`AgentInfoAction-${label}`} title={label} {...props} mi='x4'> - <Icon name={icon} size='x20' mie='x4' /> + <Button icon={icon} data-qa={`AgentInfoAction-${label}`} title={label} {...props} mi='x4'> {label} </Button> ); diff --git a/apps/meteor/client/views/omnichannel/agents/AgentsTable/RemoveAgentButton.tsx b/apps/meteor/client/views/omnichannel/agents/AgentsTable/RemoveAgentButton.tsx index 93145b54f9e3..d00138147296 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentsTable/RemoveAgentButton.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentsTable/RemoveAgentButton.tsx @@ -43,7 +43,7 @@ const RemoveAgentButton = ({ _id, reload }: RemoveAgentButtonProps): ReactElemen return ( <GenericTableCell fontScale='p2' color='hint' withTruncatedText> - <IconButton icon='trash' mini small title={t('Remove')} onClick={handleDelete} /> + <IconButton icon='trash' small title={t('Remove')} onClick={handleDelete} /> </GenericTableCell> ); }; diff --git a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursPage.js b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursPage.js index 8dbcd473bd5c..4a0c466378e6 100644 --- a/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursPage.js +++ b/apps/meteor/client/views/omnichannel/businessHours/BusinessHoursPage.js @@ -1,4 +1,4 @@ -import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import React, { lazy, useMemo } from 'react'; @@ -21,8 +21,8 @@ const BusinessHoursPage = () => { <Page> <Page.Header title={t('Business_Hours')}> <ButtonGroup> - <Button onClick={handleNew}> - <Icon name='plus' /> {t('New')} + <Button icon='plus' onClick={handleNew}> + {t('New')} </Button> </ButtonGroup> </Page.Header> diff --git a/apps/meteor/client/views/omnichannel/customFields/EditCustomFieldsPage.js b/apps/meteor/client/views/omnichannel/customFields/EditCustomFieldsPage.js index 45bbe78a9724..a6bd87f3ff63 100644 --- a/apps/meteor/client/views/omnichannel/customFields/EditCustomFieldsPage.js +++ b/apps/meteor/client/views/omnichannel/customFields/EditCustomFieldsPage.js @@ -1,4 +1,4 @@ -import { Box, Button, Icon, ButtonGroup, FieldGroup } from '@rocket.chat/fuselage'; +import { Box, Button, ButtonGroup, FieldGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback, useState } from 'react'; @@ -67,8 +67,7 @@ const EditCustomFieldsPage = ({ customField, id, reload }) => { <Page> <Page.Header title={t('Edit_Custom_Field')}> <ButtonGroup align='end'> - <Button onClick={handleReturn}> - <Icon size='x16' name='back' /> + <Button icon='back' onClick={handleReturn}> {t('Back')} </Button> <Button data-qa-id='BtnSaveEditCustomFieldsPage' primary onClick={handleSave} disabled={!canSave}> diff --git a/apps/meteor/client/views/omnichannel/customFields/NewCustomFieldsPage.js b/apps/meteor/client/views/omnichannel/customFields/NewCustomFieldsPage.js index c4185758dbc4..4c01c55ffd7c 100644 --- a/apps/meteor/client/views/omnichannel/customFields/NewCustomFieldsPage.js +++ b/apps/meteor/client/views/omnichannel/customFields/NewCustomFieldsPage.js @@ -1,4 +1,4 @@ -import { Box, Button, Icon, FieldGroup, ButtonGroup } from '@rocket.chat/fuselage'; +import { Box, Button, FieldGroup, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useCallback, useState } from 'react'; @@ -66,8 +66,7 @@ const NewCustomFieldsPage = ({ reload }) => { <Page> <Page.Header title={t('New_Custom_Field')}> <ButtonGroup> - <Button onClick={handleReturn}> - <Icon size='x16' name='back' /> + <Button icon='back' onClick={handleReturn}> {t('Back')} </Button> <Button data-qa-id='NewCustomFieldsPageButtonSave' primary onClick={handleSave} disabled={!canSave}> diff --git a/apps/meteor/client/views/omnichannel/customFields/RemoveCustomFieldButton.tsx b/apps/meteor/client/views/omnichannel/customFields/RemoveCustomFieldButton.tsx index 6277fc8cb82a..9f21fdd5d5ee 100644 --- a/apps/meteor/client/views/omnichannel/customFields/RemoveCustomFieldButton.tsx +++ b/apps/meteor/client/views/omnichannel/customFields/RemoveCustomFieldButton.tsx @@ -32,7 +32,7 @@ const RemoveCustomFieldButton = ({ _id, reload }: { _id: ILivechatCustomField['_ return ( <GenericTableCell fontScale='p2' color='hint' withTruncatedText> - <IconButton icon='trash' mini title={t('Remove')} onClick={handleDelete} /> + <IconButton icon='trash' small title={t('Remove')} onClick={handleDelete} /> </GenericTableCell> ); }; diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx index 7d574b6a23e3..2c28ad7d1aec 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentAgentsTable/RemoveAgentButton.tsx @@ -22,7 +22,7 @@ function RemoveAgentButton({ agentId, onRemove }: { agentId: string; onRemove: ( setModal(<GenericModal variant='danger' onConfirm={onRemoveAgent} onCancel={() => setModal()} confirmText={t('Delete')} />); }); - return <IconButton icon='trash' mini title={t('Remove')} onClick={handleDelete} />; + return <IconButton icon='trash' small title={t('Remove')} onClick={handleDelete} />; } export default RemoveAgentButton; diff --git a/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentItemMenu.tsx b/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentItemMenu.tsx index 31c732803657..ac163bac0257 100644 --- a/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentItemMenu.tsx +++ b/apps/meteor/client/views/omnichannel/departments/DepartmentsTable/DepartmentItemMenu.tsx @@ -60,7 +60,7 @@ const DepartmentItemMenu = ({ department, archived }: DepartmentItemMenuProps): edit: { label: ( <> - <Icon name='edit' size='x16' marginInlineEnd='x4' /> + <Icon name='edit' size='x16' mie='x4' /> {t('Edit')} </> ), @@ -70,7 +70,7 @@ const DepartmentItemMenu = ({ department, archived }: DepartmentItemMenuProps): [archived ? 'unarchive' : 'archive']: { label: ( <> - <Icon name={archived ? 'undo' : 'arrow-down-box'} size='x16' marginInlineEnd='x4' /> + <Icon name={archived ? 'undo' : 'arrow-down-box'} size='x16' mie='x4' /> {archived ? t('Unarchive') : t('Archive')} </> ), @@ -79,7 +79,7 @@ const DepartmentItemMenu = ({ department, archived }: DepartmentItemMenuProps): delete: { label: ( <Box data-tooltip={!departmentRemovalEnabled ? t('Department_Removal_Disabled') : undefined}> - <Icon name='trash' size='x16' marginInlineEnd='x4' /> + <Icon name='trash' size='x16' mie='x4' /> {t('Delete')} </Box> ), diff --git a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx index a5821382ac5b..ea3f39697b24 100644 --- a/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx +++ b/apps/meteor/client/views/omnichannel/departments/EditDepartment.tsx @@ -220,8 +220,8 @@ function EditDepartment({ data, id, title, allowedToForwardData }: EditDepartmen <Page> <Page.Header title={title}> <ButtonGroup> - <Button onClick={handleReturn}> - <Icon name='back' /> {t('Back')} + <Button icon='back' onClick={handleReturn}> + {t('Back')} </Button> <Button type='submit' form={formId} primary disabled={!isFormValid}> {t('Save')} diff --git a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfoCallButton.tsx b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfoCallButton.tsx index 5f063e958394..1e00aa6347a8 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfoCallButton.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfoCallButton.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; import React from 'react'; @@ -29,8 +29,8 @@ export const VoipInfoCallButton = ({ phoneNumber, ...props }: VoipInfoCallButton display='flex' justifyContent='center' fontSize='p2' + icon='phone' > - <Icon name='phone' size='x20' mie='4px' /> {t('Call')} </Button> ); diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js index 386ef7655233..38cc9d3e8999 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js +++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js @@ -1,4 +1,4 @@ -import { Box, Margins, Tag, Button, Icon, ButtonGroup } from '@rocket.chat/fuselage'; +import { Box, Margins, Tag, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, useUserSubscription, useTranslation, usePermission } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; @@ -175,8 +175,8 @@ function ChatInfo({ id, route }) { </ContextualbarScrollableContent> <ContextualbarFooter> <ButtonGroup stretch> - <Button onClick={onEditClick}> - <Icon name='pencil' size='x20' /> {t('Edit')} + <Button icon='pencil' onClick={onEditClick}> + {t('Edit')} </Button> </ButtonGroup> </ContextualbarFooter> diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js index ab862dd3fb2d..e04924119fa6 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js +++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js @@ -1,4 +1,4 @@ -import { Box, Margins, Tag, Button, Icon, ButtonGroup } from '@rocket.chat/fuselage'; +import { Box, Margins, Tag, Button, ButtonGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, useUserSubscription, useTranslation } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; @@ -174,8 +174,8 @@ function ChatInfoDirectory({ id, route = undefined, room }) { </ContextualbarScrollableContent> <ContextualbarFooter> <ButtonGroup stretch> - <Button onClick={onEditClick}> - <Icon name='pencil' size='x20' /> {t('Edit')} + <Button icon='pencil' onClick={onEditClick}> + {t('Edit')} </Button> </ButtonGroup> </ContextualbarFooter> diff --git a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx index 9fbde9f866bd..94e8965024b5 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/ContactTable.tsx @@ -1,4 +1,4 @@ -import { Icon, Pagination, States, StatesAction, StatesActions, StatesIcon, StatesTitle, Box } from '@rocket.chat/fuselage'; +import { Pagination, States, StatesAction, StatesActions, StatesIcon, StatesTitle, Box } from '@rocket.chat/fuselage'; import { useDebouncedState, useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -155,8 +155,7 @@ function ContactTable(): ReactElement { <StatesIcon variation='danger' name='circle-exclamation' /> <StatesTitle>{t('Connection_error')}</StatesTitle> <StatesActions> - <StatesAction onClick={() => refetch()}> - <Icon mie='x4' size='x20' name='reload' /> + <StatesAction icon='reload' onClick={() => refetch()}> {t('Reload_page')} </StatesAction> </StatesActions> diff --git a/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx b/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx index 8c9019f08e10..53b53926106a 100644 --- a/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx +++ b/apps/meteor/client/views/omnichannel/directory/contacts/contextualBar/ContactInfo.tsx @@ -1,4 +1,4 @@ -import { Box, Margins, ButtonGroup, Button, Icon, Divider } from '@rocket.chat/fuselage'; +import { Box, Margins, ButtonGroup, Button, Divider } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import type { RouteName } from '@rocket.chat/ui-contexts'; import { useToastMessageDispatch, useRoute, useTranslation, useEndpoint, usePermission, useRouter } from '@rocket.chat/ui-contexts'; @@ -180,12 +180,12 @@ const ContactInfo = ({ id: contactId, rid: roomId = '', route }: ContactInfoProp )} {showContactHistory && ( - <Button onClick={onChatHistory} mis={0} flexBasis='0'> - <Icon name='history' size='x20' /> {t('Chat_History')} + <Button icon='history' onClick={onChatHistory} mis={0} flexBasis='0'> + {t('Chat_History')} </Button> )} - <Button onClick={onEditButtonClick} flexBasis='0'> - <Icon name='pencil' size='x20' /> {t('Edit')} + <Button icon='pencil' onClick={onEditButtonClick} flexBasis='0'> + {t('Edit')} </Button> </ButtonGroup> </ContextualbarFooter> diff --git a/apps/meteor/client/views/omnichannel/installation/Installation.tsx b/apps/meteor/client/views/omnichannel/installation/Installation.tsx index 784b2e784213..be24f496e296 100644 --- a/apps/meteor/client/views/omnichannel/installation/Installation.tsx +++ b/apps/meteor/client/views/omnichannel/installation/Installation.tsx @@ -8,6 +8,7 @@ import RawText from '../../../components/RawText'; import TextCopy from '../../../components/TextCopy'; import Wrapper from './Wrapper'; +// TODO: use `CodeSnippet` Component const Installation = (): ReactElement => { const t = useTranslation(); const setting = useSetting('Site_Url') as string; diff --git a/apps/meteor/client/views/omnichannel/triggers/TriggersRow.tsx b/apps/meteor/client/views/omnichannel/triggers/TriggersRow.tsx index e871aaaad014..8a1a81d9f64a 100644 --- a/apps/meteor/client/views/omnichannel/triggers/TriggersRow.tsx +++ b/apps/meteor/client/views/omnichannel/triggers/TriggersRow.tsx @@ -53,7 +53,7 @@ const TriggersRow = ({ _id, name, description, enabled, reload }: TriggersRowPro <GenericTableCell withTruncatedText>{description}</GenericTableCell> <GenericTableCell withTruncatedText>{enabled ? t('Yes') : t('No')}</GenericTableCell> <GenericTableCell withTruncatedText> - <IconButton icon='trash' mini title={t('Remove')} onClick={handleDelete} /> + <IconButton icon='trash' small title={t('Remove')} onClick={handleDelete} /> </GenericTableCell> </GenericTableRow> ); diff --git a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx index d6788f0020f8..b5d11dfd7155 100644 --- a/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx +++ b/apps/meteor/client/views/outlookCalendar/OutlookEventsList/OutlookEventsList.tsx @@ -1,4 +1,4 @@ -import { Box, States, StatesIcon, StatesTitle, StatesSubtitle, ButtonGroup, Button, Icon } from '@rocket.chat/fuselage'; +import { Box, States, StatesIcon, StatesTitle, StatesSubtitle, ButtonGroup, Button } from '@rocket.chat/fuselage'; import { useResizeObserver } from '@rocket.chat/fuselage-hooks'; import { useTranslation, useSetting } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -116,9 +116,8 @@ const OutlookEventsList = ({ onClose, changeRoute }: OutlookEventsListProps): Re <ButtonGroup stretch> {authEnabled && <Button onClick={changeRoute}>{t('Calendar_settings')}</Button>} {outlookUrl && ( - <Button onClick={() => window.open(outlookUrl, '_blank')}> - <Icon mie='x4' name='new-window' /> - <Box is='span'>{t('Open_Outlook')}</Box> + <Button icon='new-window' onClick={() => window.open(outlookUrl, '_blank')}> + {t('Open_Outlook')} </Button> )} </ButtonGroup> diff --git a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js index 0b46f7077902..85903359dfbd 100644 --- a/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js +++ b/apps/meteor/client/views/room/contextualBar/Info/EditRoomInfo/EditChannel.js @@ -12,7 +12,6 @@ import { Button, ButtonGroup, Box, - Icon, TextAreaInput, } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; @@ -500,8 +499,7 @@ function EditChannel({ room, onClickClose, onClickBack }) { </Button> </ButtonGroup> <ButtonGroup stretch mbs='x8'> - <Button danger disabled={!canDelete || isFederated} onClick={handleDelete}> - <Icon name='trash' size='x16' /> + <Button icon='trash' danger disabled={!canDelete || isFederated} onClick={handleDelete}> {t('Delete')} </Button> </ButtonGroup> diff --git a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx index 95f2c43d1a95..d8e1ab794c8f 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomMembers/RoomMembers.tsx @@ -151,14 +151,12 @@ const RoomMembers = ({ <ContextualbarFooter> <ButtonGroup stretch> {onClickInvite && ( - <Button onClick={onClickInvite} width='50%'> - <Icon name='link' size='x20' mie='x4' /> + <Button icon='link' onClick={onClickInvite} width='50%'> {t('Invite_Link')} </Button> )} {onClickAdd && ( - <Button onClick={onClickAdd} width='50%' primary> - <Icon name='user-plus' size='x20' mie='x4' /> + <Button icon='user-plus' onClick={onClickAdd} width='50%' primary> {t('Add')} </Button> )} diff --git a/apps/meteor/ee/client/omnichannel/RemoveBusinessHourButton.js b/apps/meteor/ee/client/omnichannel/RemoveBusinessHourButton.js index 4e7143b55de7..9857eef2603c 100644 --- a/apps/meteor/ee/client/omnichannel/RemoveBusinessHourButton.js +++ b/apps/meteor/ee/client/omnichannel/RemoveBusinessHourButton.js @@ -38,7 +38,7 @@ function RemoveBusinessHourButton({ _id, type, reload }) { return ( <GenericTableCell fontScale='p2' color='hint' onClick={handleDelete} withTruncatedText> - <IconButton icon='trash' mini title={t('Remove')} onClick={handleDelete} /> + <IconButton icon='trash' small title={t('Remove')} onClick={handleDelete} /> </GenericTableCell> ); } diff --git a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx index 721b78d60dd0..9d09032c3662 100644 --- a/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx +++ b/apps/meteor/ee/client/omnichannel/cannedResponses/CannedResponseEdit.tsx @@ -1,5 +1,5 @@ import type { ILivechatDepartment, IOmnichannelCannedResponse, Serialized } from '@rocket.chat/core-typings'; -import { Button, ButtonGroup, Icon, FieldGroup } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup, FieldGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, usePermission, useEndpoint, useTranslation } from '@rocket.chat/ui-contexts'; import type { FC } from 'react'; @@ -133,8 +133,8 @@ const CannedResponseEdit: FC<{ <Page> <Page.Header title={isNew ? t('New_CannedResponse') : t('Edit_CannedResponse')}> <ButtonGroup> - <Button onClick={handleReturn}> - <Icon name='back' /> {t('Back')} + <Button icon='back' onClick={handleReturn}> + {t('Back')} </Button> <Button primary mie='none' flexGrow={1} disabled={!hasUnsavedChanges || !canSave} onClick={onSave}> {t('Save')} diff --git a/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx b/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx index 3fe5327954e3..4061214b5a0b 100644 --- a/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx +++ b/apps/meteor/ee/client/omnichannel/monitors/MonitorsTable.tsx @@ -146,7 +146,7 @@ const MonitorsTable = () => { <GenericTableCell withTruncatedText>{monitor.username}</GenericTableCell> <GenericTableCell withTruncatedText>{monitor.email}</GenericTableCell> <GenericTableCell withTruncatedText> - <IconButton icon='trash' mini title={t('Remove')} onClick={() => handleRemove(monitor.username)} /> + <IconButton icon='trash' small title={t('Remove')} onClick={() => handleRemove(monitor.username)} /> </GenericTableCell> </GenericTableRow> ))} diff --git a/apps/meteor/ee/client/omnichannel/slaPolicies/RemoveSlaButton.tsx b/apps/meteor/ee/client/omnichannel/slaPolicies/RemoveSlaButton.tsx index dbc0d1d73451..fd9ffd1eb608 100644 --- a/apps/meteor/ee/client/omnichannel/slaPolicies/RemoveSlaButton.tsx +++ b/apps/meteor/ee/client/omnichannel/slaPolicies/RemoveSlaButton.tsx @@ -34,7 +34,7 @@ const RemoveSlaButton = ({ _id, reload }: { _id: string; reload: () => void }) = return ( <GenericTableCell fontScale='p2' color='hint' withTruncatedText> - <IconButton icon='trash' mini title={t('Remove')} onClick={handleDelete} /> + <IconButton icon='trash' small title={t('Remove')} onClick={handleDelete} /> </GenericTableCell> ); }; diff --git a/apps/meteor/ee/client/omnichannel/tags/TagEdit.js b/apps/meteor/ee/client/omnichannel/tags/TagEdit.js index 7bda49cefbbf..82f9fc7e1481 100644 --- a/apps/meteor/ee/client/omnichannel/tags/TagEdit.js +++ b/apps/meteor/ee/client/omnichannel/tags/TagEdit.js @@ -1,4 +1,4 @@ -import { Field, TextInput, Button, ButtonGroup, Icon, FieldGroup } from '@rocket.chat/fuselage'; +import { Field, TextInput, Button, ButtonGroup, FieldGroup } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo } from 'react'; @@ -61,8 +61,8 @@ function TagEdit({ title, data, tagId, reload, currentDepartments, ...props }) { <Page> <Page.Header title={title}> <ButtonGroup> - <Button onClick={handleReturn}> - <Icon name='back' /> {t('Back')} + <Button icon='back' onClick={handleReturn}> + {t('Back')} </Button> <Button primary mie='none' flexGrow={1} disabled={!hasUnsavedChanges || !canSave} onClick={handleSave}> {t('Save')} diff --git a/apps/meteor/ee/client/omnichannel/units/RemoveUnitButton.tsx b/apps/meteor/ee/client/omnichannel/units/RemoveUnitButton.tsx index e86e13860c7f..fb28be048781 100644 --- a/apps/meteor/ee/client/omnichannel/units/RemoveUnitButton.tsx +++ b/apps/meteor/ee/client/omnichannel/units/RemoveUnitButton.tsx @@ -35,7 +35,7 @@ const RemoveUnitButton = ({ _id, reload }: { _id: ILivechatUnitMonitor['unitId'] return ( <GenericTableCell fontScale='p2' color='hint' withTruncatedText> - <IconButton icon='trash' mini title={t('Remove')} onClick={handleDelete} /> + <IconButton icon='trash' small title={t('Remove')} onClick={handleDelete} /> </GenericTableCell> ); }; diff --git a/apps/meteor/ee/client/omnichannel/units/UnitEdit.js b/apps/meteor/ee/client/omnichannel/units/UnitEdit.js index bf7b7db50e80..297a25d4a4c1 100644 --- a/apps/meteor/ee/client/omnichannel/units/UnitEdit.js +++ b/apps/meteor/ee/client/omnichannel/units/UnitEdit.js @@ -1,4 +1,4 @@ -import { Field, TextInput, Button, PaginatedMultiSelectFiltered, Select, ButtonGroup, Icon, FieldGroup } from '@rocket.chat/fuselage'; +import { Field, TextInput, Button, PaginatedMultiSelectFiltered, Select, ButtonGroup, FieldGroup } from '@rocket.chat/fuselage'; import { useMutableCallback, useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import { useToastMessageDispatch, useRoute, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; import React, { useMemo, useState } from 'react'; @@ -136,8 +136,8 @@ function UnitEdit({ title, data, unitId, isNew, unitMonitors, unitDepartments, r <Page> <Page.Header title={title}> <ButtonGroup> - <Button onClick={handleReturn}> - <Icon name='back' /> {t('Back')} + <Button icon='back' onClick={handleReturn}> + {t('Back')} </Button> <Button primary mie='none' flexGrow={1} disabled={!hasUnsavedChanges || !canSave} onClick={handleSave}> {t('Save')} diff --git a/apps/meteor/ee/client/views/admin/engagementDashboard/users/ContentForDays.tsx b/apps/meteor/ee/client/views/admin/engagementDashboard/users/ContentForDays.tsx index 92d41eeb2c41..4e512556f21b 100644 --- a/apps/meteor/ee/client/views/admin/engagementDashboard/users/ContentForDays.tsx +++ b/apps/meteor/ee/client/views/admin/engagementDashboard/users/ContentForDays.tsx @@ -44,10 +44,10 @@ const ContentForDays = ({ displacement, onPreviousDateClick, onNextDateClick, ti <> <Flex.Container alignItems='center' justifyContent='center'> <Box> - <IconButton icon='chevron-down' small onClick={onPreviousDateClick} style={{ verticalAlign: 'middle' }} /> + <IconButton icon='chevron-down' verticalAlign='middle' small onClick={onPreviousDateClick} /> <Flex.Item basis='50%'> <Margins inline='x8'> - <Box is='span' style={{ textAlign: 'center' }}> + <Box is='span' textAlign='center'> {formattedCurrentDate} </Box> </Margins> @@ -57,16 +57,10 @@ const ContentForDays = ({ displacement, onPreviousDateClick, onNextDateClick, ti </Flex.Container> <Flex.Container> {data ? ( - <Box style={{ height: 196 }}> + <Box height={196}> <Flex.Item align='stretch' grow={1} shrink={0}> - <Box style={{ position: 'relative' }}> - <Box - style={{ - position: 'absolute', - width: '100%', - height: '100%', - }} - > + <Box position='relative'> + <Box position='absolute' width='full' height='full'> <ResponsiveBar data={values} indexBy='day' diff --git a/apps/meteor/ee/client/views/admin/users/ReachedSeatsCapModal.tsx b/apps/meteor/ee/client/views/admin/users/ReachedSeatsCapModal.tsx index ba27595ed382..49964b18ff86 100644 --- a/apps/meteor/ee/client/views/admin/users/ReachedSeatsCapModal.tsx +++ b/apps/meteor/ee/client/views/admin/users/ReachedSeatsCapModal.tsx @@ -1,4 +1,4 @@ -import { Icon, Modal, Button, Box } from '@rocket.chat/fuselage'; +import { Modal, Button, Box } from '@rocket.chat/fuselage'; import { ExternalLink } from '@rocket.chat/ui-client'; import { useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -34,8 +34,7 @@ const ReachedSeatsCapModal = ({ members, limit, onClose, requestSeatsLink }: Rea <Modal.FooterControllers> <Button onClick={onClose}>{t('Cancel')}</Button> <ExternalLink to={requestSeatsLink}> - <Button onClick={onClose} primary> - <Icon name='new-window' size='x20' mie='x4' /> + <Button icon='new-window' onClick={onClose} primary> {t('Request')} </Button> </ExternalLink> diff --git a/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx b/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx index a900358fecd2..17b5db10e8b1 100644 --- a/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx +++ b/apps/meteor/ee/client/views/admin/users/UserPageHeaderContentWithSeatsCap.tsx @@ -1,4 +1,4 @@ -import { Button, ButtonGroup, Icon, Margins } from '@rocket.chat/fuselage'; +import { Button, ButtonGroup, Margins } from '@rocket.chat/fuselage'; import { ExternalLink } from '@rocket.chat/ui-client'; import { useSetModal, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -99,16 +99,14 @@ const UserPageHeaderContentWithSeatsCap = ({ activeUsers, maxActiveUsers }: User <SeatsCapUsage members={activeUsers} limit={maxActiveUsers} /> </Margins> <ButtonGroup> - <Button onClick={handleNewButtonClick}> - <Icon size='x20' name='user-plus' /> {t('New')} + <Button icon='user-plus' onClick={handleNewButtonClick}> + {t('New')} </Button> - <Button onClick={handleInviteButtonClick}> - <Icon size='x20' name='mail' /> {t('Invite')} + <Button icon='mail' onClick={handleInviteButtonClick}> + {t('Invite')} </Button> <ExternalLink to={seatsLinkUrl || ''}> - <Button> - <Icon size='x20' name='new-window' /> {t('Request_seats')} - </Button> + <Button icon='new-window'>{t('Request_seats')}</Button> </ExternalLink> </ButtonGroup> </> diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 8d506c26f19f..caa8f24b8c37 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -999,6 +999,7 @@ "Close_menu": "Close menu", "close-others-livechat-room": "Close Other Omnichannel Room", "close-others-livechat-room_description": "Permission to close other Omnichannel rooms", + "Close_Window": "Close Window", "Closed": "Closed", "Closed_At": "Closed at", "Closed_automatically": "Closed automatically by the system", @@ -5154,6 +5155,7 @@ "UI_Use_Name_Avatar": "Use Full Name Initials to Generate Default Avatar", "UI_Use_Real_Name": "Use Real Name", "unable-to-get-file": "Unable to get file", + "Unable_to_load_active_connections": "Unable to load active connections", "Unarchive": "Unarchive", "unarchive-room": "Unarchive Room", "unarchive-room_description": "Permission to unarchive channels", diff --git a/packages/ui-client/src/components/Card/index.ts b/packages/ui-client/src/components/Card/index.ts index 91511a7cac53..b2368088eed7 100644 --- a/packages/ui-client/src/components/Card/index.ts +++ b/packages/ui-client/src/components/Card/index.ts @@ -11,6 +11,9 @@ import Title from './CardTitle'; export const DOUBLE_COLUMN_CARD_WIDTH = 552; +/** + * @deprecated Avoid default usage, use named imports instead + */ export default Object.assign(Card, { Title, Body: CardBody, diff --git a/packages/ui-video-conf/src/VideoConfButton.tsx b/packages/ui-video-conf/src/VideoConfButton.tsx index 9273b4b1c559..cbf7c3dc7f32 100644 --- a/packages/ui-video-conf/src/VideoConfButton.tsx +++ b/packages/ui-video-conf/src/VideoConfButton.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { ReactNode, ReactElement, ButtonHTMLAttributes } from 'react'; @@ -12,8 +12,7 @@ type VideoConfButtonProps = { } & Omit<ButtonHTMLAttributes<HTMLElement>, 'ref' | 'is' | 'className' | 'size' | 'elevation'>; const VideoConfButton = ({ primary, secondary, danger, disabled, icon, children, ...props }: VideoConfButtonProps): ReactElement => ( - <Button width='100%' primary={primary} danger={danger} secondary={secondary} disabled={disabled} {...props}> - {icon && <Icon mie='x4' size='x20' name={icon} />} + <Button icon={icon} width='100%' primary={primary} danger={danger} secondary={secondary} disabled={disabled} {...props}> {children} </Button> ); diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index 8f8a8d3ecd56..bce8299a6731 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -1,4 +1,4 @@ -import { Button, Icon } from '@rocket.chat/fuselage'; +import { Button } from '@rocket.chat/fuselage'; import type { LoginService } from '@rocket.chat/ui-contexts'; import { useLoginWithService, useTranslation } from '@rocket.chat/ui-contexts'; import type { ReactElement, SetStateAction, Dispatch } from 'react'; @@ -36,6 +36,7 @@ const LoginServicesButton = <T extends LoginService>({ return ( <Button + icon={icon as IconName} className={className} onClick={handleOnClick} title={buttonLabelText && buttonLabelText !== title ? title : undefined} @@ -44,7 +45,6 @@ const LoginServicesButton = <T extends LoginService>({ display='flex' justifyContent='center' > - {icon && <Icon size='x20' mie='x4' name={icon as IconName} />} {buttonLabelText || t('Sign_in_with__provider__', { provider: title })} </Button> ); diff --git a/yarn.lock b/yarn.lock index 23ec581c7f14..206688258a58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9457,7 +9457,7 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.160, @rocket.chat/css-in-js@npm:~0.31.23-dev.165": +"@rocket.chat/css-in-js@npm:^0.31.23, @rocket.chat/css-in-js@npm:~0.31.23-dev.160, @rocket.chat/css-in-js@npm:~0.31.23-dev.166": version: 0.31.23 resolution: "@rocket.chat/css-in-js@npm:0.31.23" dependencies: @@ -9483,7 +9483,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.160, @rocket.chat/css-supports@npm:~0.31.23-dev.165": +"@rocket.chat/css-supports@npm:^0.31.23, @rocket.chat/css-supports@npm:~0.31.23-dev.160, @rocket.chat/css-supports@npm:~0.31.23-dev.166": version: 0.31.23 resolution: "@rocket.chat/css-supports@npm:0.31.23" dependencies: @@ -9699,10 +9699,10 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.341": - version: 0.32.0-dev.341 - resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.341" - checksum: f8944cbae4ea3a90c4127627e4f90ab1becc916c72ec54e1f16fd8de3e2bb8c0c6ca1dc1aa87ae7d2dc7d105f325dbbe65c262eca5719f6d237e7b2aa35a321e +"@rocket.chat/fuselage-tokens@npm:~0.32.0-dev.342": + version: 0.32.0-dev.342 + resolution: "@rocket.chat/fuselage-tokens@npm:0.32.0-dev.342" + checksum: 580b0f7c109341c092b77f1c6c642e89bf3750e0a6aa17778a840895331bb3b24a083a9e55b8a98b6182a89b71d43840afb84b2c346ba12cc922bf5abbd93b81 languageName: node linkType: hard @@ -9762,14 +9762,14 @@ __metadata: linkType: soft "@rocket.chat/fuselage@npm:next": - version: 0.32.0-dev.391 - resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.391" - dependencies: - "@rocket.chat/css-in-js": ~0.31.23-dev.165 - "@rocket.chat/css-supports": ~0.31.23-dev.165 - "@rocket.chat/fuselage-tokens": ~0.32.0-dev.341 - "@rocket.chat/memo": ~0.31.23-dev.165 - "@rocket.chat/styled": ~0.31.23-dev.165 + version: 0.32.0-dev.392 + resolution: "@rocket.chat/fuselage@npm:0.32.0-dev.392" + dependencies: + "@rocket.chat/css-in-js": ~0.31.23-dev.166 + "@rocket.chat/css-supports": ~0.31.23-dev.166 + "@rocket.chat/fuselage-tokens": ~0.32.0-dev.342 + "@rocket.chat/memo": ~0.31.23-dev.166 + "@rocket.chat/styled": ~0.31.23-dev.166 invariant: ^2.2.4 react-aria: ~3.23.1 react-keyed-flatten-children: ^1.3.0 @@ -9781,7 +9781,7 @@ __metadata: react: ^17.0.2 react-dom: ^17.0.2 react-virtuoso: 1.2.4 - checksum: 2d32285d0354bbadb876a01ad04437b28a44ef22ed76468972a99923c976e32859a9ce66f83cbf650db15e8737bbe3f4938ac6975fed8d8462f9329be37d3c0e + checksum: 340beb2d797e0c6d7a3daae453bca92f18994be376d149d6216ad25a72ba4ee6006084e6db39db2c93596520a0e953d81abda2c2f2b10623e4abd7a33a696497 languageName: node linkType: hard @@ -10005,7 +10005,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.160, @rocket.chat/memo@npm:~0.31.23-dev.165": +"@rocket.chat/memo@npm:^0.31.23, @rocket.chat/memo@npm:~0.31.23-dev.160, @rocket.chat/memo@npm:~0.31.23-dev.166": version: 0.31.23 resolution: "@rocket.chat/memo@npm:0.31.23" checksum: 070debb940749a2e4463cf767dd65c6967cea664a5bd67c22a812d611f6c3c46d6fe4bb0bf329e43dcd927493413add37c45ae3b05ec08f0b24e9d7385caebdd @@ -10831,7 +10831,7 @@ __metadata: languageName: node linkType: hard -"@rocket.chat/styled@npm:~0.31.23-dev.160, @rocket.chat/styled@npm:~0.31.23-dev.165": +"@rocket.chat/styled@npm:~0.31.23-dev.160, @rocket.chat/styled@npm:~0.31.23-dev.166": version: 0.31.23 resolution: "@rocket.chat/styled@npm:0.31.23" dependencies: From 2f41f92fa33743450ff2bfe2f001bbdb5074b96b Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi <marcos.defendi@rocket.chat> Date: Fri, 21 Jul 2023 11:18:00 -0300 Subject: [PATCH 141/149] chore: move getUrl to meteor service (#29837) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- apps/meteor/server/services/meteor/service.ts | 5 +++++ packages/core-services/src/types/IMeteor.ts | 1 + 2 files changed, 6 insertions(+) diff --git a/apps/meteor/server/services/meteor/service.ts b/apps/meteor/server/services/meteor/service.ts index 50efcd09a8a2..a7fecd5f9e85 100644 --- a/apps/meteor/server/services/meteor/service.ts +++ b/apps/meteor/server/services/meteor/service.ts @@ -16,6 +16,7 @@ import { ListenersModule } from '../../modules/listeners/listeners.module'; import notifications from '../../../app/notifications/server/lib/Notifications'; import { configureEmailInboxes } from '../../features/EmailInbox/EmailInbox'; import { use } from '../../../app/settings/server/Middleware'; +import { getURL } from '../../../app/utils/server/getURL'; type Callbacks = { added(id: string, record: object): void; @@ -277,4 +278,8 @@ export class MeteorService extends ServiceClassInternal implements IMeteor { async notifyGuestStatusChanged(token: string, status: string): Promise<void> { return Livechat.notifyGuestStatusChanged(token, status); } + + async getURL(path: string, params: Record<string, any> = {}, cloudDeepLinkUrl?: string): Promise<string> { + return getURL(path, params, cloudDeepLinkUrl); + } } diff --git a/packages/core-services/src/types/IMeteor.ts b/packages/core-services/src/types/IMeteor.ts index 80939d171c45..6b9ef7fdaffc 100644 --- a/packages/core-services/src/types/IMeteor.ts +++ b/packages/core-services/src/types/IMeteor.ts @@ -17,4 +17,5 @@ export interface IMeteor extends IServiceClass { getLoginServiceConfiguration(): Promise<any[]>; callMethodWithToken(userId: string | undefined, token: string | undefined, method: string, args: any[]): Promise<void | any>; notifyGuestStatusChanged(token: string, status: string): Promise<void>; + getURL(path: string, params?: Record<string, any>, cloudDeepLinkUrl?: string): Promise<string>; } From 6f8504362c8b9ebc1f6963cbe4954c9ee8bef03b Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto <tiago.evangelista@rocket.chat> Date: Fri, 21 Jul 2023 23:51:47 -0300 Subject: [PATCH 142/149] chore: Fix some CodeLint warnings (#29845) Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com> --- ee/packages/ddp-client/__examples__/simple.ts | 6 +++++- ee/packages/ddp-client/src/types/Account.ts | 2 +- packages/server-fetch/src/parsers.ts | 2 +- packages/ui-contexts/src/hooks/useSearchParameter.ts | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ee/packages/ddp-client/__examples__/simple.ts b/ee/packages/ddp-client/__examples__/simple.ts index 70ae77c2cf0c..f1164ed2b2ed 100644 --- a/ee/packages/ddp-client/__examples__/simple.ts +++ b/ee/packages/ddp-client/__examples__/simple.ts @@ -8,6 +8,10 @@ const run = async (url: string, token: string) => { const sdk = await DDPSDK.createAndConnect(url); try { + if (!token) { + throw new Error('Token is required'); + } + await sdk.account.loginWithToken(token); await sdk.stream('notify-room', 'GENERAL/user-activity', (args) => @@ -31,5 +35,5 @@ const run = async (url: string, token: string) => { }; (async () => { - await run('wss://unstable.rocket.chat/websocket', process.env.INSTANCE_TOKEN!); + await run('wss://unstable.rocket.chat/websocket', process.env.INSTANCE_TOKEN || ''); })(); diff --git a/ee/packages/ddp-client/src/types/Account.ts b/ee/packages/ddp-client/src/types/Account.ts index b065e03ad023..b5dde5e5ac3a 100644 --- a/ee/packages/ddp-client/src/types/Account.ts +++ b/ee/packages/ddp-client/src/types/Account.ts @@ -47,7 +47,7 @@ export class AccountImpl return; } - if (!('fields' in data) || !('username' in data.fields!)) { + if (!('fields' in data) || !(data.fields && 'username' in data.fields)) { return; } diff --git a/packages/server-fetch/src/parsers.ts b/packages/server-fetch/src/parsers.ts index c6ceb30c3659..33861c09ba41 100644 --- a/packages/server-fetch/src/parsers.ts +++ b/packages/server-fetch/src/parsers.ts @@ -2,7 +2,7 @@ import type { ExtendedFetchOptions, FetchOptions, OriginalFetchOptions } from '. function isPostOrPutOrDeleteWithBody(options?: ExtendedFetchOptions): boolean { // No method === 'get' - if (!options || !options.method) { + if (!options?.method) { return false; } const { method, body } = options; diff --git a/packages/ui-contexts/src/hooks/useSearchParameter.ts b/packages/ui-contexts/src/hooks/useSearchParameter.ts index 98740b249175..21241a53ae61 100644 --- a/packages/ui-contexts/src/hooks/useSearchParameter.ts +++ b/packages/ui-contexts/src/hooks/useSearchParameter.ts @@ -9,7 +9,7 @@ export const useSearchParameter = (name: string): string | undefined => { const getSnapshot = useCallback(() => { const searchParameters = getSearchParameters(); return searchParameters[name]; - }, [getSearchParameters]); + }, [getSearchParameters, name]); return useSyncExternalStore(subscribeToRouteChange, getSnapshot); }; From 53ee253b854ab8409e5c45d034676e0541594e97 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Sat, 22 Jul 2023 01:01:13 -0300 Subject: [PATCH 143/149] ci: cache eslint (#29891) --- .github/workflows/ci-code-check.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci-code-check.yml b/.github/workflows/ci-code-check.yml index 421c517b715a..0f9b0a112f0c 100644 --- a/.github/workflows/ci-code-check.yml +++ b/.github/workflows/ci-code-check.yml @@ -48,6 +48,15 @@ jobs: if: matrix.check == 'ts' run: yarn turbo run typecheck + - name: Cache eslint + uses: actions/cache@v3 + if: matrix.check == 'lint' + with: + path: ./apps/meteor/.eslintcache + key: eslintcache-cache-${{ runner.OS }} + restore-keys: | + eslintcache-cache + - name: Lint if: matrix.check == 'lint' run: yarn lint From 810e39410e787bc4c41e4328de7eae9342d096c7 Mon Sep 17 00:00:00 2001 From: Tasso Evangelista <tasso.evangelista@rocket.chat> Date: Sat, 22 Jul 2023 01:41:18 -0300 Subject: [PATCH 144/149] chore(eslint): ESLint configuration (#29889) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- _templates/package/new/package.json.ejs.t | 2 +- _templates/service/new/package.json.ejs.t | 2 +- apps/meteor/ee/server/services/package.json | 2 +- apps/meteor/package.json | 24 +- .../{.eslintrc => .eslintrc.json} | 0 ee/apps/account-service/package.json | 5 +- .../{.eslintrc => .eslintrc.json} | 0 ee/apps/authorization-service/package.json | 5 +- ee/apps/ddp-streamer/package.json | 5 +- ee/apps/omnichannel-transcript/package.json | 5 +- .../{.eslintrc => .eslintrc.json} | 0 ee/apps/presence-service/package.json | 5 +- ee/apps/queue-worker/package.json | 5 +- .../{.eslintrc => .eslintrc.json} | 0 ee/apps/stream-hub-service/package.json | 5 +- ee/packages/api-client/package.json | 4 +- ee/packages/ddp-client/package.json | 4 +- ee/packages/omnichannel-services/package.json | 4 +- ee/packages/pdf-worker/package.json | 4 +- .../presence/{.eslintrc => .eslintrc.json} | 0 ee/packages/presence/package.json | 8 +- ee/packages/ui-theming/package.json | 4 +- packages/account-utils/package.json | 4 +- packages/agenda/package.json | 4 +- packages/base64/package.json | 12 +- packages/cas-validate/package.json | 4 +- packages/core-services/package.json | 10 +- packages/core-typings/package.json | 4 +- packages/cron/package.json | 4 +- packages/eslint-config/package.json | 9 +- packages/favicon/package.json | 4 +- packages/fuselage-ui-kit/.eslintrc.js | 6 - packages/fuselage-ui-kit/.eslintrc.json | 3 + packages/fuselage-ui-kit/package.json | 4 +- packages/gazzodown/package.json | 10 +- packages/i18n/package.json | 8 +- packages/instance-status/package.json | 4 +- packages/livechat/package.json | 12 +- packages/log-format/package.json | 4 +- packages/mock-providers/package.json | 4 +- packages/model-typings/package.json | 4 +- packages/models/package.json | 4 +- packages/node-poplib/package.json | 4 +- packages/random/package.json | 12 +- packages/release-action/package.json | 5 +- packages/rest-typings/package.json | 4 +- packages/server-fetch/package.json | 4 +- packages/sha256/package.json | 12 +- packages/tools/package.json | 4 +- packages/ui-client/package.json | 6 +- packages/ui-composer/package.json | 6 +- packages/ui-contexts/package.json | 4 +- packages/ui-video-conf/package.json | 6 +- packages/uikit-playground/package.json | 8 +- packages/web-ui-registration/package.json | 4 +- yarn.lock | 4461 ++--------------- 56 files changed, 633 insertions(+), 4118 deletions(-) rename ee/apps/account-service/{.eslintrc => .eslintrc.json} (100%) rename ee/apps/authorization-service/{.eslintrc => .eslintrc.json} (100%) rename ee/apps/presence-service/{.eslintrc => .eslintrc.json} (100%) rename ee/apps/stream-hub-service/{.eslintrc => .eslintrc.json} (100%) rename ee/packages/presence/{.eslintrc => .eslintrc.json} (100%) delete mode 100644 packages/fuselage-ui-kit/.eslintrc.js create mode 100644 packages/fuselage-ui-kit/.eslintrc.json diff --git a/_templates/package/new/package.json.ejs.t b/_templates/package/new/package.json.ejs.t index aafc1f6e88fb..1239ec821348 100644 --- a/_templates/package/new/package.json.ejs.t +++ b/_templates/package/new/package.json.ejs.t @@ -8,7 +8,7 @@ to: packages/<%= name %>/package.json "private": true, "devDependencies": { "@types/jest": "^27.4.1", - "eslint": "^8.12.0", + "eslint": "~8.45.0", "jest": "~29.5.0", "ts-jest": "~29.0.5", "typescript": "~5.0.2" diff --git a/_templates/service/new/package.json.ejs.t b/_templates/service/new/package.json.ejs.t index d3040640a687..ca4724de539c 100644 --- a/_templates/service/new/package.json.ejs.t +++ b/_templates/service/new/package.json.ejs.t @@ -40,7 +40,7 @@ to: ee/apps/<%= name %>/package.json "@rocket.chat/eslint-config": "workspace:^", "@types/eslint": "^8.4.10", "@types/polka": "^0.5.4", - "eslint": "^8.29.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", "typescript": "~4.6.4" }, diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index dc6659b47730..02d7c58748b3 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -62,7 +62,7 @@ "pino-pretty": "^7.6.1", "pm2": "^5.2.0", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "volta": { "extends": "../../../package.json" diff --git a/apps/meteor/package.json b/apps/meteor/package.json index f3e19d15a50c..0a74e207c53e 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -62,13 +62,13 @@ "email": "support@rocket.chat" }, "devDependencies": { - "@babel/core": "^7.20.5", - "@babel/eslint-parser": "^7.22.5", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@babel/register": "^7.18.9", + "@babel/core": "~7.22.9", + "@babel/eslint-parser": "~7.22.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "~7.18.6", + "@babel/plugin-proposal-optional-chaining": "~7.21.0", + "@babel/preset-env": "~7.22.9", + "@babel/preset-react": "~7.22.5", + "@babel/register": "~7.22.5", "@faker-js/faker": "~8.0.2", "@playwright/test": "^1.22.2", "@rocket.chat/eslint-config": "workspace:^", @@ -153,8 +153,8 @@ "@types/uuid": "^8.3.4", "@types/xml-crypto": "~1.4.2", "@types/xml-encryption": "~1.2.1", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", "autoprefixer": "^9.8.8", "babel-loader": "^8.3.0", "babel-plugin-array-includes": "^2.0.3", @@ -166,7 +166,7 @@ "chai-spies": "~1.0.0", "cross-env": "^7.0.3", "emojione-assets": "^4.5.0", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-config-prettier": "~8.8.0", "eslint-plugin-anti-trojan-source": "~1.1.1", "eslint-plugin-import": "~2.26.0", @@ -204,10 +204,10 @@ "supertest": "^6.2.3", "template-file": "^6.0.1", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "dependencies": { - "@babel/runtime": "~7.22.5", + "@babel/runtime": "~7.22.6", "@bugsnag/js": "~7.20.2", "@bugsnag/plugin-react": "~7.19.0", "@google-cloud/storage": "^6.11.0", diff --git a/ee/apps/account-service/.eslintrc b/ee/apps/account-service/.eslintrc.json similarity index 100% rename from ee/apps/account-service/.eslintrc rename to ee/apps/account-service/.eslintrc.json diff --git a/ee/apps/account-service/package.json b/ee/apps/account-service/package.json index b22ed06b002f..1137f1fd93ed 100644 --- a/ee/apps/account-service/package.json +++ b/ee/apps/account-service/package.json @@ -38,11 +38,10 @@ "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/bcrypt": "^5.0.0", - "@types/eslint": "~8.40.2", "@types/polka": "^0.5.4", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "main": "./dist/ee/apps/account-service/src/service.js", "files": [ diff --git a/ee/apps/authorization-service/.eslintrc b/ee/apps/authorization-service/.eslintrc.json similarity index 100% rename from ee/apps/authorization-service/.eslintrc rename to ee/apps/authorization-service/.eslintrc.json diff --git a/ee/apps/authorization-service/package.json b/ee/apps/authorization-service/package.json index 29a6e6c95be4..c82ecc49f959 100644 --- a/ee/apps/authorization-service/package.json +++ b/ee/apps/authorization-service/package.json @@ -35,11 +35,10 @@ }, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/eslint": "~8.40.2", "@types/polka": "^0.5.4", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "main": "./dist/ee/apps/authorization-service/src/service.js", "files": [ diff --git a/ee/apps/ddp-streamer/package.json b/ee/apps/ddp-streamer/package.json index 06d2a7bb3147..f0d9d1bdbfb6 100644 --- a/ee/apps/ddp-streamer/package.json +++ b/ee/apps/ddp-streamer/package.json @@ -44,17 +44,16 @@ "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/ejson": "^2.2.0", - "@types/eslint": "~8.40.2", "@types/meteor": "^2.9.2", "@types/node": "^14.18.51", "@types/polka": "^0.5.4", "@types/sharp": "^0.30.5", "@types/uuid": "^8.3.4", "@types/ws": "^8.5.5", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "pino-pretty": "^7.6.1", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "main": "./dist/service.js", "files": [ diff --git a/ee/apps/omnichannel-transcript/package.json b/ee/apps/omnichannel-transcript/package.json index 2b4033a282d7..a738ba2d0fd8 100644 --- a/ee/apps/omnichannel-transcript/package.json +++ b/ee/apps/omnichannel-transcript/package.json @@ -41,11 +41,10 @@ "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/ui-contexts": "workspace:^", - "@types/eslint": "~8.40.2", "@types/polka": "^0.5.4", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "main": "./dist/ee/apps/omnichannel-transcript/src/service.js", "files": [ diff --git a/ee/apps/presence-service/.eslintrc b/ee/apps/presence-service/.eslintrc.json similarity index 100% rename from ee/apps/presence-service/.eslintrc rename to ee/apps/presence-service/.eslintrc.json diff --git a/ee/apps/presence-service/package.json b/ee/apps/presence-service/package.json index 4c0089e8a8e0..7697bb5e01d3 100644 --- a/ee/apps/presence-service/package.json +++ b/ee/apps/presence-service/package.json @@ -35,11 +35,10 @@ }, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/eslint": "~8.40.2", "@types/polka": "^0.5.4", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "main": "./dist/ee/apps/presence-service/src/service.js", "files": [ diff --git a/ee/apps/queue-worker/package.json b/ee/apps/queue-worker/package.json index f6b80dbe7c24..6b8c16ea5823 100644 --- a/ee/apps/queue-worker/package.json +++ b/ee/apps/queue-worker/package.json @@ -38,11 +38,10 @@ }, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "@types/eslint": "~8.40.2", "@types/polka": "^0.5.4", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "main": "./dist/ee/apps/queue-worker/src/service.js", "files": [ diff --git a/ee/apps/stream-hub-service/.eslintrc b/ee/apps/stream-hub-service/.eslintrc.json similarity index 100% rename from ee/apps/stream-hub-service/.eslintrc rename to ee/apps/stream-hub-service/.eslintrc.json diff --git a/ee/apps/stream-hub-service/package.json b/ee/apps/stream-hub-service/package.json index f9f4d7c96f8e..ea70b3822a96 100644 --- a/ee/apps/stream-hub-service/package.json +++ b/ee/apps/stream-hub-service/package.json @@ -36,11 +36,10 @@ "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", "@types/bcrypt": "^5.0.0", - "@types/eslint": "~8.40.2", "@types/polka": "^0.5.4", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "ts-node": "^10.9.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "main": "./dist/ee/apps/stream-hub-service/src/service.js", "files": [ diff --git a/ee/packages/api-client/package.json b/ee/packages/api-client/package.json index f876bc176cb2..89cb9727cbc5 100644 --- a/ee/packages/api-client/package.json +++ b/ee/packages/api-client/package.json @@ -6,11 +6,11 @@ "@swc/jest": "^0.2.26", "@types/jest": "~29.5.3", "@types/strict-uri-encode": "^2.0.0", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "jest-fetch-mock": "^3.0.3", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/ee/packages/ddp-client/package.json b/ee/packages/ddp-client/package.json index aa24abd7ead5..506d7a4bb1a1 100644 --- a/ee/packages/ddp-client/package.json +++ b/ee/packages/ddp-client/package.json @@ -6,11 +6,11 @@ "@swc/jest": "^0.2.26", "@types/jest": "~29.5.3", "@types/ws": "^8.5.5", - "eslint": "^8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "jest-environment-jsdom": "~29.6.1", "jest-websocket-mock": "^2.4.0", - "typescript": "~5.1.3", + "typescript": "~5.1.6", "ws": "^8.13.0" }, "peerDependencies": { diff --git a/ee/packages/omnichannel-services/package.json b/ee/packages/omnichannel-services/package.json index 3978a5090a6f..9b40dc48791a 100644 --- a/ee/packages/omnichannel-services/package.json +++ b/ee/packages/omnichannel-services/package.json @@ -5,10 +5,10 @@ "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "dependencies": { "@rocket.chat/core-services": "workspace:^", diff --git a/ee/packages/pdf-worker/package.json b/ee/packages/pdf-worker/package.json index f6d21946ed8d..799618d61ae7 100644 --- a/ee/packages/pdf-worker/package.json +++ b/ee/packages/pdf-worker/package.json @@ -11,12 +11,12 @@ "@types/jest": "~29.5.3", "@types/react-dom": "~17.0.20", "@types/testing-library__jest-dom": "~5.14.6", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "jest-environment-jsdom": "~29.6.1", "react-dom": "^18.2.0", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/ee/packages/presence/.eslintrc b/ee/packages/presence/.eslintrc.json similarity index 100% rename from ee/packages/presence/.eslintrc rename to ee/packages/presence/.eslintrc.json diff --git a/ee/packages/presence/package.json b/ee/packages/presence/package.json index 637170c811a3..f8be536b7356 100644 --- a/ee/packages/presence/package.json +++ b/ee/packages/presence/package.json @@ -3,17 +3,17 @@ "version": "0.0.4", "private": true, "devDependencies": { - "@babel/core": "~7.22.5", - "@babel/preset-env": "~7.22.5", + "@babel/core": "~7.22.9", + "@babel/preset-env": "~7.22.9", "@babel/preset-typescript": "~7.22.5", "@rocket.chat/apps-engine": "1.39.1", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/rest-typings": "workspace:^", "@types/node": "^14.18.51", "babel-jest": "^29.0.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint src", diff --git a/ee/packages/ui-theming/package.json b/ee/packages/ui-theming/package.json index e75c37bba089..10c4d69a1a64 100644 --- a/ee/packages/ui-theming/package.json +++ b/ee/packages/ui-theming/package.json @@ -20,7 +20,7 @@ "@storybook/testing-library": "~0.0.13", "@types/jest": "~29.5.3", "@types/react": "~17.0.62", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-plugin-anti-trojan-source": "~1.1.1", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", @@ -29,7 +29,7 @@ "react": "~17.0.2", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/account-utils/package.json b/packages/account-utils/package.json index d55c4835098b..92c0b36cd34a 100644 --- a/packages/account-utils/package.json +++ b/packages/account-utils/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/agenda/package.json b/packages/agenda/package.json index c5b92a2ce430..59c1c420c7a4 100644 --- a/packages/agenda/package.json +++ b/packages/agenda/package.json @@ -14,10 +14,10 @@ "devDependencies": { "@types/debug": "^4.1.8", "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/base64/package.json b/packages/base64/package.json index 744c101512e7..0d078be08f97 100644 --- a/packages/base64/package.json +++ b/packages/base64/package.json @@ -13,15 +13,15 @@ "test": "jest" }, "devDependencies": { - "@babel/core": "~7.22.5", - "@babel/preset-env": "~7.22.5", + "@babel/core": "~7.22.9", + "@babel/preset-env": "~7.22.9", "@rocket.chat/eslint-config": "workspace:^", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", - "eslint": "~8.43.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "volta": { "extends": "../../package.json" diff --git a/packages/cas-validate/package.json b/packages/cas-validate/package.json index ee34be0210d1..202e944e4550 100644 --- a/packages/cas-validate/package.json +++ b/packages/cas-validate/package.json @@ -5,10 +5,10 @@ "private": true, "devDependencies": { "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 3ef8d4bdb8bc..8c9340fed50f 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -3,19 +3,19 @@ "version": "0.0.4", "private": true, "devDependencies": { - "@babel/core": "^7.21.4", - "@babel/preset-env": "^7.21.4", - "@babel/preset-typescript": "^7.21.4", + "@babel/core": "~7.22.9", + "@babel/preset-env": "~7.22.9", + "@babel/preset-typescript": "~7.22.5", "@rocket.chat/eslint-config": "workspace:^", "@types/babel__core": "^7", "@types/babel__preset-env": "^7", "@types/jest": "~29.5.3", "babel-jest": "^29.5.0", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "mongodb": "^4.12.1", "prettier": "~2.8.8", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index 29f1491a0401..524ef3792af4 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -3,10 +3,10 @@ "version": "6.2.9", "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "mongodb": "^4.12.1", "prettier": "~2.8.8", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/cron/package.json b/packages/cron/package.json index b6e1a006e59d..df152b45d0e1 100644 --- a/packages/cron/package.json +++ b/packages/cron/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@types/jest": "~29.5.3", - "eslint": "^8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index a2333c444356..63f85aca14e0 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -3,11 +3,12 @@ "version": "0.5.0", "description": "Rocket.Chat's JS ESLint config", "devDependencies": { - "@babel/eslint-parser": "~7.22.5", + "@babel/eslint-parser": "~7.22.9", + "@types/eslint": "~8.44.0", "@types/prettier": "^2.6.3", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", - "eslint": "~8.43.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", + "eslint": "~8.45.0", "eslint-config-prettier": "~8.8.0", "eslint-plugin-anti-trojan-source": "~1.1.1", "eslint-plugin-import": "~2.26.0", diff --git a/packages/favicon/package.json b/packages/favicon/package.json index cf44e160d13b..04118b9dcf69 100644 --- a/packages/favicon/package.json +++ b/packages/favicon/package.json @@ -3,8 +3,8 @@ "version": "0.0.1", "private": true, "devDependencies": { - "eslint": "~8.43.0", - "typescript": "~5.1.3" + "eslint": "~8.45.0", + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/fuselage-ui-kit/.eslintrc.js b/packages/fuselage-ui-kit/.eslintrc.js deleted file mode 100644 index 07c7ef53e979..000000000000 --- a/packages/fuselage-ui-kit/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('eslint').ESLint.ConfigData} */ -const config = { - extends: ['@rocket.chat/eslint-config', '@rocket.chat/eslint-config/react'], -}; - -module.exports = config; diff --git a/packages/fuselage-ui-kit/.eslintrc.json b/packages/fuselage-ui-kit/.eslintrc.json new file mode 100644 index 000000000000..ea14af17f859 --- /dev/null +++ b/packages/fuselage-ui-kit/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["@rocket.chat/eslint-config", "@rocket.chat/eslint-config/react"] +} diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 5b3145dce4ac..b5023a2c7005 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -78,7 +78,7 @@ "@types/react-dom": "~17.0.20", "babel-loader": "~8.2.5", "cross-env": "^7.0.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "normalize.css": "^8.0.1", "npm-run-all": "^4.1.5", "prettier": "~2.8.8", @@ -86,7 +86,7 @@ "react-dom": "^17.0.2", "rimraf": "^3.0.2", "tslib": "^2.5.3", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "dependencies": { "@rocket.chat/gazzodown": "workspace:^", diff --git a/packages/gazzodown/package.json b/packages/gazzodown/package.json index e7d0cdd277f2..a64483c6735e 100644 --- a/packages/gazzodown/package.json +++ b/packages/gazzodown/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@babel/core": "~7.22.5", + "@babel/core": "~7.22.9", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/css-in-js": "next", "@rocket.chat/fuselage": "next", @@ -28,10 +28,10 @@ "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", "@types/testing-library__jest-dom": "~5.14.6", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", "babel-loader": "^8.3.0", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-plugin-anti-trojan-source": "~1.1.1", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", @@ -44,7 +44,7 @@ "react-docgen-typescript-plugin": "~1.0.5", "react-dom": "~17.0.2", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 020b4aae293b..cc2e037fb663 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -3,18 +3,18 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@babel/core": "~7.22.5", - "@babel/preset-env": "~7.22.5", + "@babel/core": "~7.22.9", + "@babel/preset-env": "~7.22.9", "@babel/preset-typescript": "~7.22.5", "@types/babel__core": "~7.20.1", "@types/babel__preset-env": "~7.9.2", "@types/jest": "~29.5.3", "babel-jest": "^29.5.0", - "eslint": "^8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", "tsup": "^6.7.0", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "build": "node ./src/index.mjs", diff --git a/packages/instance-status/package.json b/packages/instance-status/package.json index 5d3e5f30b6aa..ee45c0050eaa 100644 --- a/packages/instance-status/package.json +++ b/packages/instance-status/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "mongodb": "^4.12.1", "prettier": "~2.8.8", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/livechat/package.json b/packages/livechat/package.json index 775a44b6dad8..5ec394acc0db 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -24,8 +24,8 @@ "typecheck": "tsc -p tsconfig.typecheck.json" }, "devDependencies": { - "@babel/eslint-parser": "~7.22.5", - "@babel/preset-env": "~7.22.5", + "@babel/eslint-parser": "~7.22.9", + "@babel/preset-env": "~7.22.9", "@babel/preset-typescript": "~7.22.5", "@rocket.chat/ddp-client": "workspace:^", "@rocket.chat/eslint-config": "workspace:^", @@ -35,15 +35,15 @@ "@storybook/addon-postcss": "~2.0.0", "@storybook/preact": "~6.5.16", "@storybook/theming": "~6.5.16", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", "autoprefixer": "^9.8.8", "babel-loader": "^8.3.0", "cross-env": "^7.0.3", "css-loader": "^4.3.0", "cssnano": "^4.1.11", "desvg-loader": "^0.1.0", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-plugin-import": "~2.26.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", @@ -73,7 +73,7 @@ "stylelint": "^14.9.1", "stylelint-order": "^5.0.0", "svg-loader": "^0.0.2", - "typescript": "~5.1.3", + "typescript": "~5.1.6", "url-loader": "^4.1.1", "webpack": "~4.46.0", "webpack-cli": "~4.10.0", diff --git a/packages/log-format/package.json b/packages/log-format/package.json index c3b0a1aa404a..a3ef425e0b43 100644 --- a/packages/log-format/package.json +++ b/packages/log-format/package.json @@ -6,10 +6,10 @@ "@types/chalk": "^2.2.0", "@types/ejson": "^2.2.0", "@types/jest": "~29.5.3", - "eslint": "^8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/mock-providers/package.json b/packages/mock-providers/package.json index 1251cde3399f..b8253c2290a1 100644 --- a/packages/mock-providers/package.json +++ b/packages/mock-providers/package.json @@ -6,11 +6,11 @@ "@rocket.chat/ui-contexts": "workspace:*", "@tanstack/react-query": "^4.16.1", "@types/jest": "~29.5.3", - "eslint": "^8.12.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "react": "~17.0.2", "ts-jest": "~29.0.5", - "typescript": "~5.0.2" + "typescript": "~5.1.6" }, "peerDependencies": { "@tanstack/react-query": "*", diff --git a/packages/model-typings/package.json b/packages/model-typings/package.json index b167704d770f..8f967c5b0eeb 100644 --- a/packages/model-typings/package.json +++ b/packages/model-typings/package.json @@ -5,11 +5,11 @@ "devDependencies": { "@types/jest": "~29.5.3", "@types/node-rsa": "^1.1.1", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "mongodb": "^4.12.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/models/package.json b/packages/models/package.json index d69d02185c13..2c8ad176cf2d 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "dependencies": { "@rocket.chat/model-typings": "workspace:^" diff --git a/packages/node-poplib/package.json b/packages/node-poplib/package.json index 879a6345e484..1e96c7c07b0c 100644 --- a/packages/node-poplib/package.json +++ b/packages/node-poplib/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "test": "jest" diff --git a/packages/random/package.json b/packages/random/package.json index 297e2b72acff..459025870e57 100644 --- a/packages/random/package.json +++ b/packages/random/package.json @@ -14,16 +14,16 @@ "test": "jest" }, "devDependencies": { - "@babel/core": "~7.22.5", - "@babel/preset-env": "~7.22.5", + "@babel/core": "~7.22.9", + "@babel/preset-env": "~7.22.9", "@rocket.chat/eslint-config": "workspace:^", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", - "eslint": "~8.43.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", + "eslint": "~8.45.0", "jest": "~29.6.1", "jest-environment-jsdom": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "volta": { "extends": "../../package.json" diff --git a/packages/release-action/package.json b/packages/release-action/package.json index 648110e8ba3d..7f7dce6f5372 100644 --- a/packages/release-action/package.json +++ b/packages/release-action/package.json @@ -10,9 +10,8 @@ "main": "dist/index.js", "packageManager": "yarn@3.5.1", "devDependencies": { - "@types/eslint": "^8.40.2", "@types/node": "^16.18.36", - "typescript": "^5.1.3" + "typescript": "~5.1.6" }, "dependencies": { "@actions/core": "^1.10.0", @@ -20,7 +19,7 @@ "@actions/github": "^5.1.1", "@octokit/plugin-throttling": "^6.0.0", "@rocket.chat/eslint-config": "workspace:^", - "eslint": "^8.43.0", + "eslint": "~8.45.0", "mdast-util-to-string": "2.0.0", "remark-parse": "9.0.0", "remark-stringify": "9.0.1", diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index 470e2c756aa7..7f0437b825a0 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -4,12 +4,12 @@ "devDependencies": { "@rocket.chat/eslint-config": "workspace:^", "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "jest-environment-jsdom": "~29.6.1", "mongodb": "^4.12.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/server-fetch/package.json b/packages/server-fetch/package.json index e79d03be02b0..42d46e9479d4 100644 --- a/packages/server-fetch/package.json +++ b/packages/server-fetch/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@types/jest": "~29.5.3", - "eslint": "^8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/sha256/package.json b/packages/sha256/package.json index e8dd62ab8d1c..a6749aab4580 100644 --- a/packages/sha256/package.json +++ b/packages/sha256/package.json @@ -13,15 +13,15 @@ "test": "jest" }, "devDependencies": { - "@babel/core": "~7.22.5", - "@babel/preset-env": "~7.22.5", + "@babel/core": "~7.22.9", + "@babel/preset-env": "~7.22.9", "@rocket.chat/eslint-config": "workspace:^", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", - "eslint": "~8.43.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "volta": { "extends": "../../package.json" diff --git a/packages/tools/package.json b/packages/tools/package.json index 50bca996be6a..1a264a10fe4e 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -4,10 +4,10 @@ "private": true, "devDependencies": { "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/ui-client/package.json b/packages/ui-client/package.json index 25dca29e286a..b702ffbc4799 100644 --- a/packages/ui-client/package.json +++ b/packages/ui-client/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@babel/core": "~7.22.5", + "@babel/core": "~7.22.9", "@rocket.chat/css-in-js": "next", "@rocket.chat/fuselage": "next", "@rocket.chat/fuselage-hooks": "next", @@ -27,7 +27,7 @@ "@types/jest": "~29.5.3", "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-plugin-anti-trojan-source": "~1.1.1", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", @@ -38,7 +38,7 @@ "react-dom": "^17.0.2", "react-hook-form": "^7.30.0", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "scripts": { "lint": "eslint --ext .js,.jsx,.ts,.tsx .", diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index 659b2b7af448..2833b8f3684e 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@babel/core": "~7.22.5", + "@babel/core": "~7.22.9", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/fuselage": "next", "@rocket.chat/icons": "next", @@ -16,14 +16,14 @@ "@storybook/testing-library": "~0.0.13", "@types/babel__core": "~7.20.1", "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", "jest": "~29.6.1", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "peerDependencies": { "@rocket.chat/fuselage": "*", diff --git a/packages/ui-contexts/package.json b/packages/ui-contexts/package.json index 94a12d58abd6..19e3d86f13f5 100644 --- a/packages/ui-contexts/package.json +++ b/packages/ui-contexts/package.json @@ -11,13 +11,13 @@ "@types/react": "~17.0.62", "@types/react-dom": "~17.0.20", "@types/use-sync-external-store": "^0.0.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-plugin-react-hooks": "^4.6.0", "jest": "~29.6.1", "mongodb": "^4.12.1", "react": "~17.0.2", "ts-jest": "~29.0.5", - "typescript": "~5.1.3", + "typescript": "~5.1.6", "use-sync-external-store": "^1.2.0" }, "peerDependencies": { diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index e60ad8140d77..46bbef214242 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": true, "devDependencies": { - "@babel/core": "~7.22.5", + "@babel/core": "~7.22.9", "@rocket.chat/css-in-js": "next", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/fuselage": "next", @@ -20,14 +20,14 @@ "@storybook/testing-library": "~0.0.13", "@types/babel__core": "~7.20.1", "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "eslint-plugin-react": "~7.32.2", "eslint-plugin-react-hooks": "~4.6.0", "eslint-plugin-storybook": "~0.6.12", "jest": "~29.6.1", "react-docgen-typescript-plugin": "~1.0.5", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "peerDependencies": { "@rocket.chat/css-in-js": "*", diff --git a/packages/uikit-playground/package.json b/packages/uikit-playground/package.json index e0dd396776e0..9e217300ff40 100644 --- a/packages/uikit-playground/package.json +++ b/packages/uikit-playground/package.json @@ -40,13 +40,13 @@ "@types/react-beautiful-dnd": "^13.1.4", "@types/react-dom": "~17.0.20", "@types/use-subscription": "^1.0.0", - "@typescript-eslint/eslint-plugin": "~5.60.0", - "@typescript-eslint/parser": "~5.60.0", + "@typescript-eslint/eslint-plugin": "~5.60.1", + "@typescript-eslint/parser": "~5.60.1", "@vitejs/plugin-react": "^4.0.0", - "eslint": "^8.43.0", + "eslint": "~8.45.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.1", - "typescript": "^5.1.3", + "typescript": "~5.1.6", "vite": "^4.3.9" }, "volta": { diff --git a/packages/web-ui-registration/package.json b/packages/web-ui-registration/package.json index d5b37ffba90c..415652d9e614 100644 --- a/packages/web-ui-registration/package.json +++ b/packages/web-ui-registration/package.json @@ -9,11 +9,11 @@ "@tanstack/react-query": "^4.16.1", "@testing-library/react": "^13.3.0", "@types/jest": "~29.5.3", - "eslint": "~8.43.0", + "eslint": "~8.45.0", "jest": "~29.6.1", "react-hook-form": "^7.34.2", "ts-jest": "~29.0.5", - "typescript": "~5.1.3" + "typescript": "~5.1.6" }, "peerDependencies": { "@rocket.chat/layout": "*", diff --git a/yarn.lock b/yarn.lock index 206688258a58..4325b0eaa0b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -964,16 +964,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": - version: 7.21.4 - resolution: "@babel/code-frame@npm:7.21.4" - dependencies: - "@babel/highlight": ^7.18.6 - checksum: e5390e6ec1ac58dcef01d4f18eaf1fd2f1325528661ff6d4a5de8979588b9f5a8e852a54a91b923846f7a5c681b217f0a45c2524eb9560553160cd963b7d592c - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.22.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.5, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3": version: 7.22.5 resolution: "@babel/code-frame@npm:7.22.5" dependencies: @@ -982,24 +973,10 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/compat-data@npm:7.21.4" - checksum: 5f8b98c66f2ffba9f3c3a82c0cf354c52a0ec5ad4797b370dc32bdcd6e136ac4febe5e93d76ce76e175632e2dbf6ce9f46319aa689fcfafa41b6e49834fa4b66 - languageName: node - linkType: hard - -"@babel/compat-data@npm:^7.21.5": - version: 7.21.7 - resolution: "@babel/compat-data@npm:7.21.7" - checksum: 28747eb3fc084d088ba2db0336f52118cfa730a57bdbac81630cae1f38ad0336605b95b3390325937802f344e0b7fa25e2f1b67e3ee2d7383b877f88dee0e51c - languageName: node - linkType: hard - -"@babel/compat-data@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/compat-data@npm:7.22.5" - checksum: eb1a47ebf79ae268b4a16903e977be52629339806e248455eb9973897c503a04b701f36a9de64e19750d6e081d0561e77a514c8dc470babbeba59ae94298ed18 +"@babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.5, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/compat-data@npm:7.22.9" + checksum: bed77d9044ce948b4327b30dd0de0779fa9f3a7ed1f2d31638714ed00229fa71fc4d1617ae0eb1fad419338d3658d0e9a5a083297451e09e73e078d0347ff808 languageName: node linkType: hard @@ -1027,135 +1004,56 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.1.0, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.7.5": - version: 7.21.4 - resolution: "@babel/core@npm:7.21.4" - dependencies: - "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.4 - "@babel/helper-compilation-targets": ^7.21.4 - "@babel/helper-module-transforms": ^7.21.2 - "@babel/helpers": ^7.21.0 - "@babel/parser": ^7.21.4 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.4 - "@babel/types": ^7.21.4 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.2 - json5: ^2.2.2 - semver: ^6.3.0 - checksum: a3beebb2cc79908a02f27a07dc381bcb34e8ecc58fa99f568ad0934c49e12111fc977ee9c5b51eb7ea2da66f63155d37c4dd96b6472eaeecfc35843ccb56bf3d - languageName: node - linkType: hard - -"@babel/core@npm:^7.20.5, @babel/core@npm:~7.22.5": - version: 7.22.5 - resolution: "@babel/core@npm:7.22.5" +"@babel/core@npm:^7.1.0, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.10, @babel/core@npm:^7.12.3, @babel/core@npm:^7.21.4, @babel/core@npm:^7.7.5, @babel/core@npm:~7.22.9": + version: 7.22.9 + resolution: "@babel/core@npm:7.22.9" dependencies: "@ampproject/remapping": ^2.2.0 "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.5 - "@babel/helper-compilation-targets": ^7.22.5 - "@babel/helper-module-transforms": ^7.22.5 - "@babel/helpers": ^7.22.5 - "@babel/parser": ^7.22.5 + "@babel/generator": ^7.22.9 + "@babel/helper-compilation-targets": ^7.22.9 + "@babel/helper-module-transforms": ^7.22.9 + "@babel/helpers": ^7.22.6 + "@babel/parser": ^7.22.7 "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.5 + "@babel/traverse": ^7.22.8 "@babel/types": ^7.22.5 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.2 - semver: ^6.3.0 - checksum: 173ae426958c90c7bbd7de622c6f13fcab8aef0fac3f138e2d47bc466d1cd1f86f71ca82ae0acb9032fd8794abed8efb56fea55c031396337eaec0d673b69d56 - languageName: node - linkType: hard - -"@babel/core@npm:^7.21.4": - version: 7.21.8 - resolution: "@babel/core@npm:7.21.8" - dependencies: - "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.5 - "@babel/helper-compilation-targets": ^7.21.5 - "@babel/helper-module-transforms": ^7.21.5 - "@babel/helpers": ^7.21.5 - "@babel/parser": ^7.21.8 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.2 - json5: ^2.2.2 - semver: ^6.3.0 - checksum: f28118447355af2a90bd340e2e60699f94c8020517eba9b71bf8ebff62fa9e00d63f076e033f9dfb97548053ad62ada45fafb0d96584b1a90e8aef5a3b8241b1 + semver: ^6.3.1 + checksum: 7bf069aeceb417902c4efdaefab1f7b94adb7dea694a9aed1bda2edf4135348a080820529b1a300c6f8605740a00ca00c19b2d5e74b5dd489d99d8c11d5e56d1 languageName: node linkType: hard -"@babel/eslint-parser@npm:^7.22.5, @babel/eslint-parser@npm:~7.22.5": - version: 7.22.5 - resolution: "@babel/eslint-parser@npm:7.22.5" +"@babel/eslint-parser@npm:~7.22.9": + version: 7.22.9 + resolution: "@babel/eslint-parser@npm:7.22.9" dependencies: "@nicolo-ribaudo/eslint-scope-5-internals": 5.1.1-v1 eslint-visitor-keys: ^2.1.0 - semver: ^6.3.0 + semver: ^6.3.1 peerDependencies: "@babel/core": ">=7.11.0" eslint: ^7.5.0 || ^8.0.0 - checksum: d259a5c6bb11d2b99316a1aafb85be56fd290e2b7076b386a026cd0f8db4b27c0bf0c733bfa2006bb88d38abef323fc2c1352a3521e6e9865f8b205fef6ac845 - languageName: node - linkType: hard - -"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.21.4, @babel/generator@npm:^7.7.2": - version: 7.21.4 - resolution: "@babel/generator@npm:7.21.4" - dependencies: - "@babel/types": ^7.21.4 - "@jridgewell/gen-mapping": ^0.3.2 - "@jridgewell/trace-mapping": ^0.3.17 - jsesc: ^2.5.1 - checksum: 9ffbb526a53bb8469b5402f7b5feac93809b09b2a9f82fcbfcdc5916268a65dae746a1f2479e03ba4fb0776facd7c892191f63baa61ab69b2cfdb24f7b92424d + checksum: 4f417796c803056aad2c8fa69b8a7a78a1fdacc307d95702f22894cab42b83554e47de7d0b3cfbee667f25014bca0179f859aa86ceb684b09803192e1200b48d languageName: node linkType: hard -"@babel/generator@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/generator@npm:7.21.5" - dependencies: - "@babel/types": ^7.21.5 - "@jridgewell/gen-mapping": ^0.3.2 - "@jridgewell/trace-mapping": ^0.3.17 - jsesc: ^2.5.1 - checksum: 78af737b9dd701d4c657f9731880430fa1c177767b562f4e8a330a7fe72a4abe857e3d24de4e6d9dafc1f6a11f894162d27e523d7e5948ff9e3925a0ce9867c4 - languageName: node - linkType: hard - -"@babel/generator@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/generator@npm:7.22.5" +"@babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.22.7, @babel/generator@npm:^7.22.9, @babel/generator@npm:^7.7.2": + version: 7.22.9 + resolution: "@babel/generator@npm:7.22.9" dependencies: "@babel/types": ^7.22.5 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: efa64da70ca88fe69f05520cf5feed6eba6d30a85d32237671488cc355fdc379fe2c3246382a861d49574c4c2f82a317584f8811e95eb024e365faff3232b49d + checksum: 7c9d2c58b8d5ac5e047421a6ab03ec2ff5d9a5ff2c2212130a0055e063ac349e0b19d435537d6886c999771aef394832e4f54cd9fc810100a7f23d982f6af06b languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: 88ccd15ced475ef2243fdd3b2916a29ea54c5db3cd0cfabf9d1d29ff6e63b7f7cd1c27264137d7a40ac2e978b9b9a542c332e78f40eb72abe737a7400788fc1b - languageName: node - linkType: hard - -"@babel/helper-annotate-as-pure@npm:^7.22.5": +"@babel/helper-annotate-as-pure@npm:^7.18.6, @babel/helper-annotate-as-pure@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" dependencies: @@ -1164,16 +1062,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.18.6" - dependencies: - "@babel/helper-explode-assignable-expression": ^7.18.6 - "@babel/types": ^7.18.6 - checksum: c4d71356e0adbc20ce9fe7c1e1181ff65a78603f8bba7615745f0417fed86bad7dc0a54a840bc83667c66709b3cb3721edcb9be0d393a298ce4e9eb6d085f3c1 - languageName: node - linkType: hard - "@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.5" @@ -1183,89 +1071,22 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/helper-compilation-targets@npm:7.21.4" - dependencies: - "@babel/compat-data": ^7.21.4 - "@babel/helper-validator-option": ^7.21.0 - browserslist: ^4.21.3 - lru-cache: ^5.1.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: bf9c7d3e7e6adff9222c05d898724cd4ee91d7eb9d52222c7ad2a22955620c2872cc2d9bdf0e047df8efdb79f4e3af2a06b53f509286145feccc4d10ddc318be - languageName: node - linkType: hard - -"@babel/helper-compilation-targets@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-compilation-targets@npm:7.21.5" +"@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/helper-compilation-targets@npm:7.22.9" dependencies: - "@babel/compat-data": ^7.21.5 - "@babel/helper-validator-option": ^7.21.0 - browserslist: ^4.21.3 - lru-cache: ^5.1.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 0edecb9c970ddc22ebda1163e77a7f314121bef9e483e0e0d9a5802540eed90d5855b6bf9bce03419b35b2e07c323e62d0353b153fa1ca34f17dbba897a83c25 - languageName: node - linkType: hard - -"@babel/helper-compilation-targets@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-compilation-targets@npm:7.22.5" - dependencies: - "@babel/compat-data": ^7.22.5 + "@babel/compat-data": ^7.22.9 "@babel/helper-validator-option": ^7.22.5 - browserslist: ^4.21.3 + browserslist: ^4.21.9 lru-cache: ^5.1.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: a479460615acffa0f4fd0a29b740eafb53a93694265207d23a6038ccd18d183a382cacca515e77b7c9b042c3ba80b0aca0da5f1f62215140e81660d2cf721b68 - languageName: node - linkType: hard - -"@babel/helper-create-class-features-plugin@npm:^7.17.6, @babel/helper-create-class-features-plugin@npm:^7.18.6": - version: 7.21.8 - resolution: "@babel/helper-create-class-features-plugin@npm:7.21.8" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-member-expression-to-functions": ^7.21.5 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-replace-supers": ^7.21.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/helper-split-export-declaration": ^7.18.6 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 26b978bd2e741259c0f4a1cc37521ad58728c50d28fe2fc8041d4381497e13a0b686a10e170246855eaf3af08886862e9d93fc27994ef914e13fca0d73efdcb8 - languageName: node - linkType: hard - -"@babel/helper-create-class-features-plugin@npm:^7.21.0": - version: 7.21.4 - resolution: "@babel/helper-create-class-features-plugin@npm:7.21.4" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-member-expression-to-functions": ^7.21.0 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-replace-supers": ^7.20.7 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/helper-split-export-declaration": ^7.18.6 + semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0 - checksum: 9123ca80a4894aafdb1f0bc08e44f6be7b12ed1fbbe99c501b484f9b1a17ff296b6c90c18c222047d53c276f07f17b4de857946fa9d0aa207023b03e4cc716f2 + checksum: ea0006c6a93759025f4a35a25228ae260538c9f15023e8aac2a6d45ca68aef4cf86cfc429b19af9a402cbdd54d5de74ad3fbcf6baa7e48184dc079f1a791e178 languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.22.5": +"@babel/helper-create-class-features-plugin@npm:^7.17.6, @babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0, @babel/helper-create-class-features-plugin@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-create-class-features-plugin@npm:7.22.5" dependencies: @@ -1284,32 +1105,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-regexp-features-plugin@npm:^7.18.6": - version: 7.21.8 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.8" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - regexpu-core: ^5.3.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 04a686b5897c86339395894c0a9a1ffdce2facaba5173ce7b0a894f775f984ba70d2fa227d309f2be54f7f1286ebd1a0a7051a8b1829521595e4064ee062af65 - languageName: node - linkType: hard - -"@babel/helper-create-regexp-features-plugin@npm:^7.20.5": - version: 7.21.4 - resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.4" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - regexpu-core: ^5.3.1 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 78334865db2cd1d64d103bd0d96dee2818b0387d10aa973c084e245e829df32652bca530803e397b7158af4c02b9b21d5a9601c29bdfbb8d54a3d4ad894e067b - languageName: node - linkType: hard - -"@babel/helper-create-regexp-features-plugin@npm:^7.22.5": +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.5" dependencies: @@ -1340,42 +1136,18 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.3": - version: 0.3.3 - resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" - dependencies: - "@babel/helper-compilation-targets": ^7.17.7 - "@babel/helper-plugin-utils": ^7.16.7 - debug: ^4.1.1 - lodash.debounce: ^4.0.8 - resolve: ^1.14.2 - semver: ^6.1.2 - peerDependencies: - "@babel/core": ^7.4.0-0 - checksum: 8e3fe75513302e34f6d92bd67b53890e8545e6c5bca8fe757b9979f09d68d7e259f6daea90dc9e01e332c4f8781bda31c5fe551c82a277f9bc0bec007aed497c - languageName: node - linkType: hard - -"@babel/helper-define-polyfill-provider@npm:^0.4.0": - version: 0.4.0 - resolution: "@babel/helper-define-polyfill-provider@npm:0.4.0" +"@babel/helper-define-polyfill-provider@npm:^0.4.2": + version: 0.4.2 + resolution: "@babel/helper-define-polyfill-provider@npm:0.4.2" dependencies: - "@babel/helper-compilation-targets": ^7.17.7 - "@babel/helper-plugin-utils": ^7.16.7 + "@babel/helper-compilation-targets": ^7.22.6 + "@babel/helper-plugin-utils": ^7.22.5 debug: ^4.1.1 lodash.debounce: ^4.0.8 resolve: ^1.14.2 - semver: ^6.1.2 peerDependencies: - "@babel/core": ^7.4.0-0 - checksum: 5dca4c5e78457c5ced366bea601efa4e8c69bf5d53b0fe540283897575c49b1b88191c8ef062110de9046e886703ed3270fcda3a87f0886cdbb549204d3ff63f - languageName: node - linkType: hard - -"@babel/helper-environment-visitor@npm:^7.18.9, @babel/helper-environment-visitor@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-environment-visitor@npm:7.21.5" - checksum: e436af7b62956e919066448013a3f7e2cd0b51010c26c50f790124dcd350be81d5597b4e6ed0a4a42d098a27de1e38561cd7998a116a42e7899161192deac9a6 + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 1f6dec0c5d0876d278fe15b71238eccc5f74c4e2efa2c78aaafa8bc2cc96336b8e68d94cd1a78497356c96e8b91b8c1f4452179820624d1702aee2f9832e6569 languageName: node linkType: hard @@ -1386,25 +1158,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-explode-assignable-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: 225cfcc3376a8799023d15dc95000609e9d4e7547b29528c7f7111a0e05493ffb12c15d70d379a0bb32d42752f340233c4115bded6d299bc0c3ab7a12be3d30f - languageName: node - linkType: hard - -"@babel/helper-function-name@npm:^7.18.9, @babel/helper-function-name@npm:^7.19.0, @babel/helper-function-name@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-function-name@npm:7.21.0" - dependencies: - "@babel/template": ^7.20.7 - "@babel/types": ^7.21.0 - checksum: d63e63c3e0e3e8b3138fa47b0cd321148a300ef12b8ee951196994dcd2a492cc708aeda94c2c53759a5c9177fffaac0fd8778791286746f72a000976968daf4e - languageName: node - linkType: hard - "@babel/helper-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-function-name@npm:7.22.5" @@ -1415,15 +1168,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-hoist-variables@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f - languageName: node - linkType: hard - "@babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" @@ -1433,24 +1177,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.20.7, @babel/helper-member-expression-to-functions@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-member-expression-to-functions@npm:7.21.0" - dependencies: - "@babel/types": ^7.21.0 - checksum: 49cbb865098195fe82ba22da3a8fe630cde30dcd8ebf8ad5f9a24a2b685150c6711419879cf9d99b94dad24cff9244d8c2a890d3d7ec75502cd01fe58cff5b5d - languageName: node - linkType: hard - -"@babel/helper-member-expression-to-functions@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-member-expression-to-functions@npm:7.21.5" - dependencies: - "@babel/types": ^7.21.5 - checksum: c404b4a0271c640b7dc8c34af7b683c70a43200259e02330cfc02e79e6b271e9227f35554cd6ad015eabcfa1fea75b9d0b87b69f3d1e6c2af6edd224060b1732 - languageName: node - linkType: hard - "@babel/helper-member-expression-to-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-member-expression-to-functions@npm:7.22.5" @@ -1460,16 +1186,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/helper-module-imports@npm:7.21.4" - dependencies: - "@babel/types": ^7.21.4 - checksum: bd330a2edaafeb281fbcd9357652f8d2666502567c0aad71db926e8499c773c9ea9c10dfaae30122452940326d90c8caff5c649ed8e1bf15b23f858758d3abc6 - languageName: node - linkType: hard - -"@babel/helper-module-imports@npm:^7.22.5": +"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-imports@npm:7.22.5" dependencies: @@ -1478,60 +1195,18 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-module-transforms@npm:7.21.5" - dependencies: - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-module-imports": ^7.21.4 - "@babel/helper-simple-access": ^7.21.5 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/helper-validator-identifier": ^7.19.1 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - checksum: 1ccfc88830675a5d485d198e918498f9683cdd46f973fdd4fe1c85b99648fb70f87fca07756c7a05dc201bd9b248c74ced06ea80c9991926ac889f53c3659675 - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.2": - version: 7.21.2 - resolution: "@babel/helper-module-transforms@npm:7.21.2" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-simple-access": ^7.20.2 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/helper-validator-identifier": ^7.19.1 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.2 - "@babel/types": ^7.21.2 - checksum: 8a1c129a4f90bdf97d8b6e7861732c9580f48f877aaaafbc376ce2482febebcb8daaa1de8bc91676d12886487603f8c62a44f9e90ee76d6cac7f9225b26a49e1 - languageName: node - linkType: hard - -"@babel/helper-module-transforms@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-module-transforms@npm:7.22.5" +"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.22.5, @babel/helper-module-transforms@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/helper-module-transforms@npm:7.22.9" dependencies: "@babel/helper-environment-visitor": ^7.22.5 "@babel/helper-module-imports": ^7.22.5 "@babel/helper-simple-access": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 "@babel/helper-validator-identifier": ^7.22.5 - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.5 - "@babel/types": ^7.22.5 - checksum: 8985dc0d971fd17c467e8b84fe0f50f3dd8610e33b6c86e5b3ca8e8859f9448bcc5c84e08a2a14285ef388351c0484797081c8f05a03770bf44fc27bf4900e68 - languageName: node - linkType: hard - -"@babel/helper-optimise-call-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: e518fe8418571405e21644cfb39cf694f30b6c47b10b006609a92469ae8b8775cbff56f0b19732343e2ea910641091c5a2dc73b56ceba04e116a33b0f8bd2fbd + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 2751f77660518cf4ff027514d6f4794f04598c6393be7b04b8e46c6e21606e11c19f3f57ab6129a9c21bacdf8b3ffe3af87bb401d972f34af2d0ffde02ac3001 languageName: node linkType: hard @@ -1551,34 +1226,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.21.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": - version: 7.21.5 - resolution: "@babel/helper-plugin-utils@npm:7.21.5" - checksum: 6f086e9a84a50ea7df0d5639c8f9f68505af510ea3258b3c8ac8b175efdfb7f664436cb48996f71791a1350ba68f47ad3424131e8e718c5e2ad45564484cbb36 - languageName: node - linkType: hard - -"@babel/helper-plugin-utils@npm:^7.22.5": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.13.0, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.22.5 resolution: "@babel/helper-plugin-utils@npm:7.22.5" checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-wrap-function": ^7.18.9 - "@babel/types": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 4be6076192308671b046245899b703ba090dbe7ad03e0bea897bb2944ae5b88e5e85853c9d1f83f643474b54c578d8ac0800b80341a86e8538264a725fbbefec - languageName: node - linkType: hard - "@babel/helper-remap-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-remap-async-to-generator@npm:7.22.5" @@ -1593,35 +1247,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.18.6, @babel/helper-replace-supers@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-replace-supers@npm:7.21.5" - dependencies: - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-member-expression-to-functions": ^7.21.5 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - checksum: 4fd343e6f90533743d8e8a1f42e50377b3d6b27f524a27eb97ff28f075e4e55cca2383adb1b0973de358b08022aef0fec4c8d69711e1da43bf9b887b5a893677 - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/helper-replace-supers@npm:7.20.7" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-member-expression-to-functions": ^7.20.7 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.20.7 - "@babel/types": ^7.20.7 - checksum: b8e0087c9b0c1446e3c6f3f72b73b7e03559c6b570e2cfbe62c738676d9ebd8c369a708cf1a564ef88113b4330750a50232ee1131d303d478b7a5e65e46fbc7c - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.22.5": +"@babel/helper-replace-supers@npm:^7.16.7, @babel/helper-replace-supers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-replace-supers@npm:7.22.5" dependencies: @@ -1635,24 +1261,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-simple-access@npm:^7.20.2": - version: 7.20.2 - resolution: "@babel/helper-simple-access@npm:7.20.2" - dependencies: - "@babel/types": ^7.21.5 - checksum: ad1e96ee2e5f654ffee2369a586e5e8d2722bf2d8b028a121b4c33ebae47253f64d420157b9f0a8927aea3a9e0f18c0103e74fdd531815cf3650a0a4adca11a1 - languageName: node - linkType: hard - -"@babel/helper-simple-access@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-simple-access@npm:7.21.5" - dependencies: - "@babel/types": ^7.21.5 - checksum: ad212beaa24be3864c8c95bee02f840222457ccf5419991e2d3e3e39b0f75b77e7e857e0bf4ed428b1cd97acefc87f3831bdb0b9696d5ad0557421f398334fc3 - languageName: node - linkType: hard - "@babel/helper-simple-access@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-simple-access@npm:7.22.5" @@ -1662,16 +1270,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0": - version: 7.20.0 - resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.20.0" - dependencies: - "@babel/types": ^7.20.0 - checksum: 34da8c832d1c8a546e45d5c1d59755459ffe43629436707079989599b91e8c19e50e73af7a4bd09c95402d389266731b0d9c5f69e372d8ebd3a709c05c80d7dd - languageName: node - linkType: hard - -"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0, @babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" dependencies: @@ -1680,45 +1279,22 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-split-export-declaration@npm:7.18.6" - dependencies: - "@babel/types": ^7.18.6 - checksum: c6d3dede53878f6be1d869e03e9ffbbb36f4897c7cc1527dc96c56d127d834ffe4520a6f7e467f5b6f3c2843ea0e81a7819d66ae02f707f6ac057f3d57943a2b - languageName: node - linkType: hard - -"@babel/helper-split-export-declaration@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-split-export-declaration@npm:7.22.5" +"@babel/helper-split-export-declaration@npm:^7.22.5, @babel/helper-split-export-declaration@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helper-split-export-declaration@npm:7.22.6" dependencies: "@babel/types": ^7.22.5 - checksum: d10e05a02f49c1f7c578cea63d2ac55356501bbf58856d97ac9bfde4957faee21ae97c7f566aa309e38a256eef58b58e5b670a7f568b362c00e93dfffe072650 + checksum: e141cace583b19d9195f9c2b8e17a3ae913b7ee9b8120246d0f9ca349ca6f03cb2c001fd5ec57488c544347c0bb584afec66c936511e447fd20a360e591ac921 languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.19.4, @babel/helper-string-parser@npm:^7.22.5": +"@babel/helper-string-parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-string-parser@npm:7.22.5" checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helper-string-parser@npm:7.21.5" - checksum: 36c0ded452f3858e67634b81960d4bde1d1cd2a56b82f4ba2926e97864816021c885f111a7cf81de88a0ed025f49d84a393256700e9acbca2d99462d648705d8 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": - version: 7.19.1 - resolution: "@babel/helper-validator-identifier@npm:7.19.1" - checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a - languageName: node - linkType: hard - "@babel/helper-validator-identifier@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-identifier@npm:7.22.5" @@ -1726,32 +1302,13 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.18.6, @babel/helper-validator-option@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helper-validator-option@npm:7.21.0" - checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 - languageName: node - linkType: hard - -"@babel/helper-validator-option@npm:^7.22.5": +"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-option@npm:7.22.5" checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 languageName: node linkType: hard -"@babel/helper-wrap-function@npm:^7.18.9": - version: 7.19.0 - resolution: "@babel/helper-wrap-function@npm:7.19.0" - dependencies: - "@babel/helper-function-name": ^7.19.0 - "@babel/template": ^7.18.10 - "@babel/traverse": ^7.19.0 - "@babel/types": ^7.19.0 - checksum: 2453a6b134f12cc779179188c4358a66252c29b634a8195c0cf626e17f9806c3c4c40e159cd8056c2ec82b69b9056a088014fa43d6ccc1aca67da8d9605da8fd - languageName: node - linkType: hard - "@babel/helper-wrap-function@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-wrap-function@npm:7.22.5" @@ -1764,51 +1321,18 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/helpers@npm:7.21.0" - dependencies: - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.0 - "@babel/types": ^7.21.0 - checksum: 9370dad2bb665c551869a08ac87c8bdafad53dbcdce1f5c5d498f51811456a3c005d9857562715151a0f00b2e912ac8d89f56574f837b5689f5f5072221cdf54 - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/helpers@npm:7.21.5" - dependencies: - "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.5 - "@babel/types": ^7.21.5 - checksum: a6f74b8579713988e7f5adf1a986d8b5255757632ba65b2552f0f609ead5476edb784044c7e4b18f3681ee4818ca9d08c41feb9bd4e828648c25a00deaa1f9e4 - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helpers@npm:7.22.5" +"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helpers@npm:7.22.6" dependencies: "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.5 + "@babel/traverse": ^7.22.6 "@babel/types": ^7.22.5 - checksum: a96e785029dff72f171190943df895ab0f76e17bf3881efd630bc5fae91215042d1c2e9ed730e8e4adf4da6f28b24bd1f54ed93b90ffbca34c197351872a084e + checksum: 5c1f33241fe7bf7709868c2105134a0a86dca26a0fbd508af10a89312b1f77ca38ebae43e50be3b208613c5eacca1559618af4ca236f0abc55d294800faeff30 languageName: node linkType: hard -"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/highlight@npm:7.18.6" - dependencies: - "@babel/helper-validator-identifier": ^7.18.6 - chalk: ^2.0.0 - js-tokens: ^4.0.0 - checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 - languageName: node - linkType: hard - -"@babel/highlight@npm:^7.22.5": +"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.22.5": version: 7.22.5 resolution: "@babel/highlight@npm:7.22.5" dependencies: @@ -1819,41 +1343,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/parser@npm:7.21.4" - bin: - parser: ./bin/babel-parser.js - checksum: de610ecd1bff331766d0c058023ca11a4f242bfafefc42caf926becccfb6756637d167c001987ca830dd4b34b93c629a4cef63f8c8c864a8564cdfde1989ac77 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.21.5, @babel/parser@npm:^7.21.8": - version: 7.21.8 - resolution: "@babel/parser@npm:7.21.8" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7": + version: 7.22.7 + resolution: "@babel/parser@npm:7.22.7" bin: parser: ./bin/babel-parser.js - checksum: 1b9a820fedfb6ef179e6ffa1dbc080808882949dec68340a616da2aa354af66ea2886bd68e61bd444d270aa0b24ad6273e3cfaf17d6878c34bf2521becacb353 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/parser@npm:7.22.5" - bin: - parser: ./bin/babel-parser.js - checksum: 470ebba516417ce8683b36e2eddd56dcfecb32c54b9bb507e28eb76b30d1c3e618fd0cfeee1f64d8357c2254514e1a19e32885cfb4e73149f4ae875436a6d59c - languageName: node - linkType: hard - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 845bd280c55a6a91d232cfa54eaf9708ec71e594676fe705794f494bb8b711d833b752b59d1a5c154695225880c23dbc9cab0e53af16fd57807976cd3ff41b8d + checksum: 02209ddbd445831ee8bf966fdf7c29d189ed4b14343a68eb2479d940e7e3846340d7cc6bd654a5f3d87d19dc84f49f50a58cf9363bee249dc5409ff3ba3dab54 languageName: node linkType: hard @@ -1868,19 +1363,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/plugin-proposal-optional-chaining": ^7.20.7 - peerDependencies: - "@babel/core": ^7.13.0 - checksum: d610f532210bee5342f5b44a12395ccc6d904e675a297189bc1e401cc185beec09873da523466d7fec34ae1574f7a384235cba1ccc9fe7b89ba094167897c845 - languageName: node - linkType: hard - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.5" @@ -1894,21 +1376,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" - dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 - "@babel/plugin-syntax-async-generators": ^7.8.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 - languageName: node - linkType: hard - -"@babel/plugin-proposal-class-properties@npm:^7.12.1, @babel/plugin-proposal-class-properties@npm:^7.18.6": +"@babel/plugin-proposal-class-properties@npm:^7.12.1": version: 7.18.6 resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: @@ -1920,19 +1388,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-class-static-block@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" - dependencies: - "@babel/helper-create-class-features-plugin": ^7.21.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-class-static-block": ^7.14.5 - peerDependencies: - "@babel/core": ^7.12.0 - checksum: 236c0ad089e7a7acab776cc1d355330193314bfcd62e94e78f2df35817c6144d7e0e0368976778afd6b7c13e70b5068fa84d7abbf967d4f182e60d03f9ef802b - languageName: node - linkType: hard - "@babel/plugin-proposal-decorators@npm:^7.12.12": version: 7.17.8 resolution: "@babel/plugin-proposal-decorators@npm:7.17.8" @@ -1948,18 +1403,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-dynamic-import": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f - languageName: node - linkType: hard - "@babel/plugin-proposal-export-default-from@npm:^7.12.1": version: 7.16.7 resolution: "@babel/plugin-proposal-export-default-from@npm:7.16.7" @@ -1972,43 +1415,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef - languageName: node - linkType: hard - -"@babel/plugin-proposal-json-strings@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-json-strings": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 - languageName: node - linkType: hard - -"@babel/plugin-proposal-logical-assignment-operators@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: cdd7b8136cc4db3f47714d5266f9e7b592a2ac5a94a5878787ce08890e97c8ab1ca8e94b27bfeba7b0f2b1549a026d9fc414ca2196de603df36fb32633bbdc19 - languageName: node - linkType: hard - -"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.12.1, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.12.1, @babel/plugin-proposal-nullish-coalescing-operator@npm:~7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" dependencies: @@ -2020,18 +1427,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-numeric-separator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-numeric-separator": ^7.10.4 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: f370ea584c55bf4040e1f78c80b4eeb1ce2e6aaa74f87d1a48266493c33931d0b6222d8cee3a082383d6bb648ab8d6b7147a06f974d3296ef3bc39c7851683ec - languageName: node - linkType: hard - "@babel/plugin-proposal-object-rest-spread@npm:7.12.1": version: 7.12.1 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.12.1" @@ -2045,7 +1440,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.12.1, @babel/plugin-proposal-object-rest-spread@npm:^7.20.7": +"@babel/plugin-proposal-object-rest-spread@npm:^7.12.1": version: 7.20.7 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" dependencies: @@ -2060,19 +1455,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7b5b39fb5d8d6d14faad6cb68ece5eeb2fd550fb66b5af7d7582402f974f5bc3684641f7c192a5a57e0f59acfae4aada6786be1eba030881ddc590666eff4d1e - languageName: node - linkType: hard - -"@babel/plugin-proposal-optional-chaining@npm:^7.12.7, @babel/plugin-proposal-optional-chaining@npm:^7.18.9, @babel/plugin-proposal-optional-chaining@npm:^7.20.7, @babel/plugin-proposal-optional-chaining@npm:^7.21.0": +"@babel/plugin-proposal-optional-chaining@npm:^7.12.7, @babel/plugin-proposal-optional-chaining@npm:~7.21.0": version: 7.21.0 resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" dependencies: @@ -2085,7 +1468,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-methods@npm:^7.12.1, @babel/plugin-proposal-private-methods@npm:^7.18.6": +"@babel/plugin-proposal-private-methods@npm:^7.12.1": version: 7.18.6 resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" dependencies: @@ -2106,7 +1489,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.12.1, @babel/plugin-proposal-private-property-in-object@npm:^7.21.0": +"@babel/plugin-proposal-private-property-in-object@npm:^7.12.1": version: 7.21.0 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0" dependencies: @@ -2120,7 +1503,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": +"@babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": version: 7.18.6 resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" dependencies: @@ -2231,17 +1614,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.20.0": - version: 7.20.0 - resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" - dependencies: - "@babel/helper-plugin-utils": ^7.19.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 6a86220e0aae40164cd3ffaf80e7c076a1be02a8f3480455dddbae05fda8140f429290027604df7a11b3f3f124866e8a6d69dbfa1dda61ee7377b920ad144d5b - languageName: node - linkType: hard - "@babel/plugin-syntax-import-assertions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-import-assertions@npm:7.22.5" @@ -2297,18 +1669,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.18.6, @babel/plugin-syntax-jsx@npm:^7.21.4, @babel/plugin-syntax-jsx@npm:^7.7.2": - version: 7.21.4 - resolution: "@babel/plugin-syntax-jsx@npm:7.21.4" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: bb7309402a1d4e155f32aa0cf216e1fa8324d6c4cfd248b03280028a015a10e46b6efd6565f515f8913918a3602b39255999c06046f7d4b8a5106be2165d724a - languageName: node - linkType: hard - -"@babel/plugin-syntax-jsx@npm:^7.22.5": +"@babel/plugin-syntax-jsx@npm:^7.22.5, @babel/plugin-syntax-jsx@npm:^7.7.2": version: 7.22.5 resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" dependencies: @@ -2407,18 +1768,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.20.0, @babel/plugin-syntax-typescript@npm:^7.7.2": - version: 7.21.4 - resolution: "@babel/plugin-syntax-typescript@npm:7.21.4" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: a59ce2477b7ae8c8945dc37dda292fef9ce46a6507b3d76b03ce7f3a6c9451a6567438b20a78ebcb3955d04095fd1ccd767075a863f79fcc30aa34dcfa441fe0 - languageName: node - linkType: hard - -"@babel/plugin-syntax-typescript@npm:^7.22.5": +"@babel/plugin-syntax-typescript@npm:^7.22.5, @babel/plugin-syntax-typescript@npm:^7.7.2": version: 7.22.5 resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" dependencies: @@ -2441,18 +1791,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-arrow-functions@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: b43cabe3790c2de7710abe32df9a30005eddb2050dadd5d122c6872f679e5710e410f1b90c8f99a2aff7b614cccfecf30e7fd310236686f60d3ed43fd80b9847 - languageName: node - linkType: hard - -"@babel/plugin-transform-arrow-functions@npm:^7.22.5": +"@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-arrow-functions@npm:7.22.5" dependencies: @@ -2463,9 +1802,9 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.5" +"@babel/plugin-transform-async-generator-functions@npm:^7.22.7": + version: 7.22.7 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.7" dependencies: "@babel/helper-environment-visitor": ^7.22.5 "@babel/helper-plugin-utils": ^7.22.5 @@ -2473,20 +1812,7 @@ __metadata: "@babel/plugin-syntax-async-generators": ^7.8.4 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 32890b69ec5627eb46ee8e084bddc6b98d85b66cae5e015f3a23924611a759789d2ff836406605f5293b5c2bad306b20cb1f5b7a46ed549b07bfec634bcd31f9 - languageName: node - linkType: hard - -"@babel/plugin-transform-async-to-generator@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" - dependencies: - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 + checksum: 57cd2cce3fb696dadf00e88f168683df69e900b92dadeae07429243c43bc21d5ccdc0c2db61cf5c37bd0fbd893fc455466bef6babe4aa5b79d9cb8ba89f40ae7 languageName: node linkType: hard @@ -2503,17 +1829,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0a0df61f94601e3666bf39f2cc26f5f7b22a94450fb93081edbed967bd752ce3f81d1227fefd3799f5ee2722171b5e28db61379234d1bb85b6ec689589f99d7e - languageName: node - linkType: hard - "@babel/plugin-transform-block-scoped-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" @@ -2525,18 +1840,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 15aacaadbecf96b53a750db1be4990b0d89c7f5bc3e1794b63b49fb219638c1fd25d452d15566d7e5ddf5b5f4e1a0a0055c35c1c7aee323c7b114bf49f66f4b0 - languageName: node - linkType: hard - -"@babel/plugin-transform-block-scoping@npm:^7.22.5": +"@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoping@npm:7.22.5" dependencies: @@ -2572,53 +1876,22 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-classes@npm:7.21.0" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-compilation-targets": ^7.20.7 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-optimise-call-expression": ^7.18.6 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-replace-supers": ^7.20.7 - "@babel/helper-split-export-declaration": ^7.18.6 - globals: ^11.1.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 088ae152074bd0e90f64659169255bfe50393e637ec8765cb2a518848b11b0299e66b91003728fd0a41563a6fdc6b8d548ece698a314fd5447f5489c22e466b7 - languageName: node - linkType: hard - -"@babel/plugin-transform-classes@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-classes@npm:7.22.5" +"@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/plugin-transform-classes@npm:7.22.6" dependencies: "@babel/helper-annotate-as-pure": ^7.22.5 - "@babel/helper-compilation-targets": ^7.22.5 + "@babel/helper-compilation-targets": ^7.22.6 "@babel/helper-environment-visitor": ^7.22.5 "@babel/helper-function-name": ^7.22.5 "@babel/helper-optimise-call-expression": ^7.22.5 "@babel/helper-plugin-utils": ^7.22.5 "@babel/helper-replace-supers": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 globals: ^11.1.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 124b1b79180524cc9d08155cecde92c7f2ab0db02cbe0f8befa187ef3c7320909ce1a6d6daf5ce73e8330f9b40cf9991f424c6e572b8dddc1f14e2758fa80d20 - languageName: node - linkType: hard - -"@babel/plugin-transform-computed-properties@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-computed-properties@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/template": ^7.20.7 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: be70e54bda8b469146459f429e5f2bd415023b87b2d5af8b10e48f465ffb02847a3ed162ca60378c004b82db848e4d62e90010d41ded7e7176b6d8d1c2911139 + checksum: 8380e855c01033dbc7460d9acfbc1fc37c880350fa798c2de8c594ef818ade0e4c96173ec72f05f2a4549d8d37135e18cb62548352d51557b45a0fb4388d2f3f languageName: node linkType: hard @@ -2634,18 +1907,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 43ebbe0bfa20287e34427be7c2200ce096c20913775ea75268fb47fe0e55f9510800587e6052c42fe6dffa0daaad95dd465c3e312fd1ef9785648384c45417ac - languageName: node - linkType: hard - -"@babel/plugin-transform-destructuring@npm:^7.22.5": +"@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-destructuring@npm:7.22.5" dependencies: @@ -2656,19 +1918,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.4.4": - version: 7.18.6 - resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: cbe5d7063eb8f8cca24cd4827bc97f5641166509e58781a5f8aa47fb3d2d786ce4506a30fca2e01f61f18792783a5cb5d96bf5434c3dd1ad0de8c9cc625a53da - languageName: node - linkType: hard - -"@babel/plugin-transform-dotall-regex@npm:^7.22.5": +"@babel/plugin-transform-dotall-regex@npm:^7.22.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": version: 7.22.5 resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" dependencies: @@ -2680,17 +1930,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 220bf4a9fec5c4d4a7b1de38810350260e8ea08481bf78332a464a21256a95f0df8cd56025f346238f09b04f8e86d4158fafc9f4af57abaef31637e3b58bd4fe - languageName: node - linkType: hard - "@babel/plugin-transform-duplicate-keys@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" @@ -2714,18 +1953,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7f70222f6829c82a36005508d34ddbe6fd0974ae190683a8670dd6ff08669aaf51fef2209d7403f9bd543cb2d12b18458016c99a6ed0332ccedb3ea127b01229 - languageName: node - linkType: hard - "@babel/plugin-transform-exponentiation-operator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.22.5" @@ -2762,18 +1989,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-for-of@npm:7.21.0" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2f3f86ca1fab2929fcda6a87e4303d5c635b5f96dc9a45fd4ca083308a3020c79ac33b9543eb4640ef2b79f3586a00ab2d002a7081adb9e9d7440dce30781034 - languageName: node - linkType: hard - -"@babel/plugin-transform-for-of@npm:^7.22.5": +"@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-for-of@npm:7.22.5" dependencies: @@ -2784,19 +2000,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-function-name@npm:7.18.9" - dependencies: - "@babel/helper-compilation-targets": ^7.18.9 - "@babel/helper-function-name": ^7.18.9 - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 62dd9c6cdc9714704efe15545e782ee52d74dc73916bf954b4d3bee088fb0ec9e3c8f52e751252433656c09f744b27b757fc06ed99bcde28e8a21600a1d8e597 - languageName: node - linkType: hard - "@babel/plugin-transform-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-function-name@npm:7.22.5" @@ -2822,17 +2025,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-literals@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3458dd2f1a47ac51d9d607aa18f3d321cbfa8560a985199185bed5a906bb0c61ba85575d386460bac9aed43fdd98940041fae5a67dff286f6f967707cff489f8 - languageName: node - linkType: hard - "@babel/plugin-transform-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-literals@npm:7.22.5" @@ -2856,17 +2048,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 35a3d04f6693bc6b298c05453d85ee6e41cc806538acb6928427e0e97ae06059f97d2f07d21495fcf5f70d3c13a242e2ecbd09d5c1fcb1b1a73ff528dcb0b695 - languageName: node - linkType: hard - "@babel/plugin-transform-member-expression-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.22.5" @@ -2878,18 +2059,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.20.11": - version: 7.20.11 - resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" - dependencies: - "@babel/helper-module-transforms": ^7.20.11 - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 23665c1c20c8f11c89382b588fb9651c0756d130737a7625baeaadbd3b973bc5bfba1303bedffa8fb99db1e6d848afb01016e1df2b69b18303e946890c790001 - languageName: node - linkType: hard - "@babel/plugin-transform-modules-amd@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-amd@npm:7.22.5" @@ -2902,19 +2071,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.21.2": - version: 7.21.2 - resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.2" - dependencies: - "@babel/helper-module-transforms": ^7.21.2 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-simple-access": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 65aa06e3e3792f39b99eb5f807034693ff0ecf80438580f7ae504f4c4448ef04147b1889ea5e6f60f3ad4a12ebbb57c6f1f979a249dadbd8d11fe22f4441918b - languageName: node - linkType: hard - "@babel/plugin-transform-modules-commonjs@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5" @@ -2928,20 +2084,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.20.11": - version: 7.20.11 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11" - dependencies: - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-module-transforms": ^7.20.11 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-validator-identifier": ^7.19.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 4546c47587f88156d66c7eb7808e903cf4bb3f6ba6ac9bc8e3af2e29e92eb9f0b3f44d52043bfd24eb25fa7827fd7b6c8bfeac0cac7584e019b87e1ecbd0e673 - languageName: node - linkType: hard - "@babel/plugin-transform-modules-systemjs@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.5" @@ -2956,18 +2098,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" - dependencies: - "@babel/helper-module-transforms": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c3b6796c6f4579f1ba5ab0cdcc73910c1e9c8e1e773c507c8bb4da33072b3ae5df73c6d68f9126dab6e99c24ea8571e1563f8710d7c421fac1cde1e434c20153 - languageName: node - linkType: hard - "@babel/plugin-transform-modules-umd@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-umd@npm:7.22.5" @@ -2980,18 +2110,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.20.5": - version: 7.20.5 - resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.20.5" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.20.5 - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 528c95fb1087e212f17e1c6456df041b28a83c772b9c93d2e407c9d03b72182b0d9d126770c1d6e0b23aab052599ceaf25ed6a2c0627f4249be34a83f6fae853 - languageName: node - linkType: hard - "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" @@ -3004,17 +2122,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-new-target@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: bd780e14f46af55d0ae8503b3cb81ca86dcc73ed782f177e74f498fff934754f9e9911df1f8f3bd123777eed7c1c1af4d66abab87c8daae5403e7719a6b845d1 - languageName: node - linkType: hard - "@babel/plugin-transform-new-target@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-new-target@npm:7.22.5" @@ -3065,18 +2172,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-object-super@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/helper-replace-supers": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0fcb04e15deea96ae047c21cb403607d49f06b23b4589055993365ebd7a7d7541334f06bf9642e90075e66efce6ebaf1eb0ef066fbbab802d21d714f1aac3aef - languageName: node - linkType: hard - "@babel/plugin-transform-object-super@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-object-super@npm:7.22.5" @@ -3101,31 +2196,20 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.22.5" +"@babel/plugin-transform-optional-chaining@npm:^7.22.5, @babel/plugin-transform-optional-chaining@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.22.6" dependencies: "@babel/helper-plugin-utils": ^7.22.5 "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 57b9c05fb22ae881b8a334b184fc6ee966661ed5d1eb4eed8c2fb9a12e68150d90b229efcb1aa777e246999830844fee06d7365f8bb4bb262fdcd23876ff3ea2 - languageName: node - linkType: hard - -"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-parameters@npm:7.21.3" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c92128d7b1fcf54e2cab186c196bbbf55a9a6de11a83328dc2602649c9dc6d16ef73712beecd776cd49bfdc624b5f56740f4a53568d3deb9505ec666bc869da3 + checksum: 9713f7920ed04090c149fc5ec024dd1638e8b97aa4ae3753b93072d84103b8de380afb96d6cf03e53b285420db4f705f3ac13149c6fd54f322b61dc19e33c54f languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.22.5": +"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-parameters@npm:7.22.5" dependencies: @@ -3162,17 +2246,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 1c16e64de554703f4b547541de2edda6c01346dd3031d4d29e881aa7733785cd26d53611a4ccf5353f4d3e69097bb0111c0a93ace9e683edd94fea28c4484144 - languageName: node - linkType: hard - "@babel/plugin-transform-property-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-property-literals@npm:7.22.5" @@ -3184,17 +2257,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-display-name@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-display-name@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 51c087ab9e41ef71a29335587da28417536c6f816c292e092ffc0e0985d2f032656801d4dd502213ce32481f4ba6c69402993ffa67f0818a07606ff811e4be49 - languageName: node - linkType: hard - "@babel/plugin-transform-react-display-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-display-name@npm:7.22.5" @@ -3206,17 +2268,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-development@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-jsx-development@npm:7.18.6" - dependencies: - "@babel/plugin-transform-react-jsx": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ec9fa65db66f938b75c45e99584367779ac3e0af8afc589187262e1337c7c4205ea312877813ae4df9fb93d766627b8968d74ac2ba702e4883b1dbbe4953ecee - languageName: node - linkType: hard - "@babel/plugin-transform-react-jsx-development@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx-development@npm:7.22.5" @@ -3250,22 +2301,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.12.12, @babel/plugin-transform-react-jsx@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-jsx@npm:7.18.6" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-module-imports": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-jsx": ^7.18.6 - "@babel/types": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 46129eaf1ab7a7a73e3e8c9d9859b630f5b381c5e19fb1559e2db7b943a7825b6715ad950623fb03fe7bd31ed618ce1d0bd539b13fa030a50c39d5a873a5ba00 - languageName: node - linkType: hard - -"@babel/plugin-transform-react-jsx@npm:^7.22.5": +"@babel/plugin-transform-react-jsx@npm:^7.12.12, @babel/plugin-transform-react-jsx@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx@npm:7.22.5" dependencies: @@ -3280,18 +2316,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-pure-annotations@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.18.6" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 97c4873d409088f437f9084d084615948198dd87fc6723ada0e7e29c5a03623c2f3e03df3f52e7e7d4d23be32a08ea00818bff302812e48713c706713bd06219 - languageName: node - linkType: hard - "@babel/plugin-transform-react-pure-annotations@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.22.5" @@ -3304,18 +2328,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.20.5": - version: 7.20.5 - resolution: "@babel/plugin-transform-regenerator@npm:7.20.5" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - regenerator-transform: ^0.15.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 13164861e71fb23d84c6270ef5330b03c54d5d661c2c7468f28e21c4f8598558ca0c8c3cb1d996219352946e849d270a61372bc93c8fbe9676e78e3ffd0dea07 - languageName: node - linkType: hard - "@babel/plugin-transform-regenerator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-regenerator@npm:7.22.5" @@ -3328,17 +2340,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 0738cdc30abdae07c8ec4b233b30c31f68b3ff0eaa40eddb45ae607c066127f5fa99ddad3c0177d8e2832e3a7d3ad115775c62b431ebd6189c40a951b867a80c - languageName: node - linkType: hard - "@babel/plugin-transform-reserved-words@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" @@ -3350,18 +2351,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.12.1, @babel/plugin-transform-shorthand-properties@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: b8e4e8acc2700d1e0d7d5dbfd4fdfb935651913de6be36e6afb7e739d8f9ca539a5150075a0f9b79c88be25ddf45abb912fe7abf525f0b80f5b9d9860de685d7 - languageName: node - linkType: hard - -"@babel/plugin-transform-shorthand-properties@npm:^7.22.5": +"@babel/plugin-transform-shorthand-properties@npm:^7.12.1, @babel/plugin-transform-shorthand-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" dependencies: @@ -3372,19 +2362,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-spread@npm:7.20.7" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 8ea698a12da15718aac7489d4cde10beb8a3eea1f66167d11ab1e625033641e8b328157fd1a0b55dd6531933a160c01fc2e2e61132a385cece05f26429fd0cc2 - languageName: node - linkType: hard - -"@babel/plugin-transform-spread@npm:^7.22.5": +"@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-spread@npm:7.22.5" dependencies: @@ -3396,17 +2374,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 68ea18884ae9723443ffa975eb736c8c0d751265859cd3955691253f7fee37d7a0f7efea96c8a062876af49a257a18ea0ed5fea0d95a7b3611ce40f7ee23aee3 - languageName: node - linkType: hard - "@babel/plugin-transform-sticky-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" @@ -3418,18 +2385,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 3d2fcd79b7c345917f69b92a85bdc3ddd68ce2c87dc70c7d61a8373546ccd1f5cb8adc8540b49dfba08e1b82bb7b3bbe23a19efdb2b9c994db2db42906ca9fb2 - languageName: node - linkType: hard - -"@babel/plugin-transform-template-literals@npm:^7.22.5": +"@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" dependencies: @@ -3440,17 +2396,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: e754e0d8b8a028c52e10c148088606e3f7a9942c57bd648fc0438e5b4868db73c386a5ed47ab6d6f0594aae29ee5ffc2ffc0f7ebee7fae560a066d6dea811cd4 - languageName: node - linkType: hard - "@babel/plugin-transform-typeof-symbol@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" @@ -3462,20 +2407,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.21.3": - version: 7.21.3 - resolution: "@babel/plugin-transform-typescript@npm:7.21.3" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-create-class-features-plugin": ^7.21.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-typescript": ^7.20.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c16fd577bf43f633deb76fca2a8527d8ae25968c8efdf327c1955472c3e0257e62992473d1ad7f9ee95379ce2404699af405ea03346055adadd3478ad0ecd117 - languageName: node - linkType: hard - "@babel/plugin-transform-typescript@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typescript@npm:7.22.5" @@ -3490,17 +2421,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.18.10": - version: 7.21.5 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.21.5" - dependencies: - "@babel/helper-plugin-utils": ^7.21.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 6504d642d0449a275191b624bd94d3e434ae154e610bf2f0e3c109068b287d2474f68e1da64b47f21d193cd67b27ee4643877d530187670565cac46e29fd257d - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-escapes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.5" @@ -3524,18 +2444,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: d9e18d57536a2d317fb0b7c04f8f55347f3cfacb75e636b4c6fa2080ab13a3542771b5120e726b598b815891fc606d1472ac02b749c69fd527b03847f22dc25e - languageName: node - linkType: hard - "@babel/plugin-transform-unicode-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" @@ -3560,97 +2468,12 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/preset-env@npm:7.21.4" +"@babel/preset-env@npm:^7.12.11, @babel/preset-env@npm:~7.22.9": + version: 7.22.9 + resolution: "@babel/preset-env@npm:7.22.9" dependencies: - "@babel/compat-data": ^7.21.4 - "@babel/helper-compilation-targets": ^7.21.4 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-validator-option": ^7.21.0 - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.20.7 - "@babel/plugin-proposal-async-generator-functions": ^7.20.7 - "@babel/plugin-proposal-class-properties": ^7.18.6 - "@babel/plugin-proposal-class-static-block": ^7.21.0 - "@babel/plugin-proposal-dynamic-import": ^7.18.6 - "@babel/plugin-proposal-export-namespace-from": ^7.18.9 - "@babel/plugin-proposal-json-strings": ^7.18.6 - "@babel/plugin-proposal-logical-assignment-operators": ^7.20.7 - "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 - "@babel/plugin-proposal-numeric-separator": ^7.18.6 - "@babel/plugin-proposal-object-rest-spread": ^7.20.7 - "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 - "@babel/plugin-proposal-optional-chaining": ^7.21.0 - "@babel/plugin-proposal-private-methods": ^7.18.6 - "@babel/plugin-proposal-private-property-in-object": ^7.21.0 - "@babel/plugin-proposal-unicode-property-regex": ^7.18.6 - "@babel/plugin-syntax-async-generators": ^7.8.4 - "@babel/plugin-syntax-class-properties": ^7.12.13 - "@babel/plugin-syntax-class-static-block": ^7.14.5 - "@babel/plugin-syntax-dynamic-import": ^7.8.3 - "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - "@babel/plugin-syntax-import-assertions": ^7.20.0 - "@babel/plugin-syntax-json-strings": ^7.8.3 - "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 - "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 - "@babel/plugin-syntax-numeric-separator": ^7.10.4 - "@babel/plugin-syntax-object-rest-spread": ^7.8.3 - "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 - "@babel/plugin-syntax-optional-chaining": ^7.8.3 - "@babel/plugin-syntax-private-property-in-object": ^7.14.5 - "@babel/plugin-syntax-top-level-await": ^7.14.5 - "@babel/plugin-transform-arrow-functions": ^7.20.7 - "@babel/plugin-transform-async-to-generator": ^7.20.7 - "@babel/plugin-transform-block-scoped-functions": ^7.18.6 - "@babel/plugin-transform-block-scoping": ^7.21.0 - "@babel/plugin-transform-classes": ^7.21.0 - "@babel/plugin-transform-computed-properties": ^7.20.7 - "@babel/plugin-transform-destructuring": ^7.21.3 - "@babel/plugin-transform-dotall-regex": ^7.18.6 - "@babel/plugin-transform-duplicate-keys": ^7.18.9 - "@babel/plugin-transform-exponentiation-operator": ^7.18.6 - "@babel/plugin-transform-for-of": ^7.21.0 - "@babel/plugin-transform-function-name": ^7.18.9 - "@babel/plugin-transform-literals": ^7.18.9 - "@babel/plugin-transform-member-expression-literals": ^7.18.6 - "@babel/plugin-transform-modules-amd": ^7.20.11 - "@babel/plugin-transform-modules-commonjs": ^7.21.2 - "@babel/plugin-transform-modules-systemjs": ^7.20.11 - "@babel/plugin-transform-modules-umd": ^7.18.6 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.20.5 - "@babel/plugin-transform-new-target": ^7.18.6 - "@babel/plugin-transform-object-super": ^7.18.6 - "@babel/plugin-transform-parameters": ^7.21.3 - "@babel/plugin-transform-property-literals": ^7.18.6 - "@babel/plugin-transform-regenerator": ^7.20.5 - "@babel/plugin-transform-reserved-words": ^7.18.6 - "@babel/plugin-transform-shorthand-properties": ^7.18.6 - "@babel/plugin-transform-spread": ^7.20.7 - "@babel/plugin-transform-sticky-regex": ^7.18.6 - "@babel/plugin-transform-template-literals": ^7.18.9 - "@babel/plugin-transform-typeof-symbol": ^7.18.9 - "@babel/plugin-transform-unicode-escapes": ^7.18.10 - "@babel/plugin-transform-unicode-regex": ^7.18.6 - "@babel/preset-modules": ^0.1.5 - "@babel/types": ^7.21.4 - babel-plugin-polyfill-corejs2: ^0.3.3 - babel-plugin-polyfill-corejs3: ^0.6.0 - babel-plugin-polyfill-regenerator: ^0.4.1 - core-js-compat: ^3.25.1 - semver: ^6.3.0 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 1e328674c4b39e985fa81e5a8eee9aaab353dea4ff1f28f454c5e27a6498c762e25d42e827f5bfc9d7acf6c9b8bc317b5283aa7c83d9fd03c1a89e5c08f334f9 - languageName: node - linkType: hard - -"@babel/preset-env@npm:^7.20.2, @babel/preset-env@npm:~7.22.5": - version: 7.22.5 - resolution: "@babel/preset-env@npm:7.22.5" - dependencies: - "@babel/compat-data": ^7.22.5 - "@babel/helper-compilation-targets": ^7.22.5 + "@babel/compat-data": ^7.22.9 + "@babel/helper-compilation-targets": ^7.22.9 "@babel/helper-plugin-utils": ^7.22.5 "@babel/helper-validator-option": ^7.22.5 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.22.5 @@ -3675,13 +2498,13 @@ __metadata: "@babel/plugin-syntax-top-level-await": ^7.14.5 "@babel/plugin-syntax-unicode-sets-regex": ^7.18.6 "@babel/plugin-transform-arrow-functions": ^7.22.5 - "@babel/plugin-transform-async-generator-functions": ^7.22.5 + "@babel/plugin-transform-async-generator-functions": ^7.22.7 "@babel/plugin-transform-async-to-generator": ^7.22.5 "@babel/plugin-transform-block-scoped-functions": ^7.22.5 "@babel/plugin-transform-block-scoping": ^7.22.5 "@babel/plugin-transform-class-properties": ^7.22.5 "@babel/plugin-transform-class-static-block": ^7.22.5 - "@babel/plugin-transform-classes": ^7.22.5 + "@babel/plugin-transform-classes": ^7.22.6 "@babel/plugin-transform-computed-properties": ^7.22.5 "@babel/plugin-transform-destructuring": ^7.22.5 "@babel/plugin-transform-dotall-regex": ^7.22.5 @@ -3706,7 +2529,7 @@ __metadata: "@babel/plugin-transform-object-rest-spread": ^7.22.5 "@babel/plugin-transform-object-super": ^7.22.5 "@babel/plugin-transform-optional-catch-binding": ^7.22.5 - "@babel/plugin-transform-optional-chaining": ^7.22.5 + "@babel/plugin-transform-optional-chaining": ^7.22.6 "@babel/plugin-transform-parameters": ^7.22.5 "@babel/plugin-transform-private-methods": ^7.22.5 "@babel/plugin-transform-private-property-in-object": ^7.22.5 @@ -3724,14 +2547,14 @@ __metadata: "@babel/plugin-transform-unicode-sets-regex": ^7.22.5 "@babel/preset-modules": ^0.1.5 "@babel/types": ^7.22.5 - babel-plugin-polyfill-corejs2: ^0.4.3 - babel-plugin-polyfill-corejs3: ^0.8.1 - babel-plugin-polyfill-regenerator: ^0.5.0 - core-js-compat: ^3.30.2 - semver: ^6.3.0 + babel-plugin-polyfill-corejs2: ^0.4.4 + babel-plugin-polyfill-corejs3: ^0.8.2 + babel-plugin-polyfill-regenerator: ^0.5.1 + core-js-compat: ^3.31.0 + semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6d9d09010ababef2ab48c8830770b2a8f45d6cce51db0924a98b0d95a5b1248a99ee07ee61cb5446d8b05b562db99a8af30b3ed194546419fb9b2889b8fd1ed3 + checksum: 6caa2897bbda30c6932aed0a03827deb1337c57108050c9f97dc9a857e1533c7125b168b6d70b9d191965bf05f9f233f0ad20303080505dff7ce39740aaa759d languageName: node linkType: hard @@ -3763,23 +2586,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.12.10": - version: 7.18.6 - resolution: "@babel/preset-react@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/helper-validator-option": ^7.18.6 - "@babel/plugin-transform-react-display-name": ^7.18.6 - "@babel/plugin-transform-react-jsx": ^7.18.6 - "@babel/plugin-transform-react-jsx-development": ^7.18.6 - "@babel/plugin-transform-react-pure-annotations": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 540d9cf0a0cc0bb07e6879994e6fb7152f87dafbac880b56b65e2f528134c7ba33e0cd140b58700c77b2ebf4c81fa6468fed0ba391462d75efc7f8c1699bb4c3 - languageName: node - linkType: hard - -"@babel/preset-react@npm:^7.18.6": +"@babel/preset-react@npm:^7.12.10, @babel/preset-react@npm:~7.22.5": version: 7.22.5 resolution: "@babel/preset-react@npm:7.22.5" dependencies: @@ -3795,22 +2602,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.12.7, @babel/preset-typescript@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/preset-typescript@npm:7.21.4" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-validator-option": ^7.21.0 - "@babel/plugin-syntax-jsx": ^7.21.4 - "@babel/plugin-transform-modules-commonjs": ^7.21.2 - "@babel/plugin-transform-typescript": ^7.21.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 83b2f2bf7be3a970acd212177525f58bbb1f2e042b675a47d021a675ae27cf00b6b6babfaf3ae5c980592c9ed1b0712e5197796b691905d25c99f9006478ea06 - languageName: node - linkType: hard - -"@babel/preset-typescript@npm:~7.22.5": +"@babel/preset-typescript@npm:^7.12.7, @babel/preset-typescript@npm:~7.22.5": version: 7.22.5 resolution: "@babel/preset-typescript@npm:7.22.5" dependencies: @@ -3825,22 +2617,7 @@ __metadata: languageName: node linkType: hard -"@babel/register@npm:^7.12.1": - version: 7.18.9 - resolution: "@babel/register@npm:7.18.9" - dependencies: - clone-deep: ^4.0.1 - find-cache-dir: ^2.0.0 - make-dir: ^2.1.0 - pirates: ^4.0.5 - source-map-support: ^0.5.16 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 4aeaff97e061a397f632659082ba86c539ef8194697b236d991c10d1c2ea8f73213d3b5b3b2c24625951a1ef726b7a7d2e70f70ffcb37f79ef0c1a745eebef21 - languageName: node - linkType: hard - -"@babel/register@npm:^7.18.9": +"@babel/register@npm:^7.12.1, @babel/register@npm:~7.22.5": version: 7.22.5 resolution: "@babel/register@npm:7.22.5" dependencies: @@ -3871,39 +2648,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": - version: 7.21.0 - resolution: "@babel/runtime@npm:7.21.0" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.20.1": - version: 7.21.5 - resolution: "@babel/runtime@npm:7.21.5" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 358f2779d3187f5c67ad302e8f8d435412925d0b991d133c7d4a7b1ddd5a3fda1b6f34537cb64628dfd96a27ae46df105bed3895b8d754b88cacdded8d1129dd - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.20.13, @babel/runtime@npm:~7.22.5": - version: 7.22.5 - resolution: "@babel/runtime@npm:7.22.5" - dependencies: - regenerator-runtime: ^0.13.11 - checksum: 12a50b7de2531beef38840d17af50c55a094253697600cee255311222390c68eed704829308d4fd305e1b3dfbce113272e428e9d9d45b1730e0fede997eaceb1 - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.20.6": - version: 7.20.7 - resolution: "@babel/runtime@npm:7.20.7" +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2, @babel/runtime@npm:~7.22.6": + version: 7.22.6 + resolution: "@babel/runtime@npm:7.22.6" dependencies: regenerator-runtime: ^0.13.11 - checksum: 4629ce5c46f06cca9cfb9b7fc00d48003335a809888e2b91ec2069a2dcfbfef738480cff32ba81e0b7c290f8918e5c22ddcf2b710001464ee84ba62c7e32a3a3 + checksum: e585338287c4514a713babf4fdb8fc2a67adcebab3e7723a739fc62c79cfda875b314c90fd25f827afb150d781af97bc16c85bfdbfa2889f06053879a1ddb597 languageName: node linkType: hard @@ -3916,18 +2666,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.12.7, @babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.3.3": - version: 7.20.7 - resolution: "@babel/template@npm:7.20.7" - dependencies: - "@babel/code-frame": ^7.18.6 - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 - checksum: 2eb1a0ab8d415078776bceb3473d07ab746e6bb4c2f6ca46ee70efb284d75c4a32bb0cd6f4f4946dec9711f9c0780e8e5d64b743208deac6f8e9858afadc349e - languageName: node - linkType: hard - -"@babel/template@npm:^7.22.5": +"@babel/template@npm:^7.12.7, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" dependencies: @@ -3938,83 +2677,25 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.19.0, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/traverse@npm:7.21.4" - dependencies: - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.4 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.21.4 - "@babel/types": ^7.21.4 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: f22f067c2d9b6497abf3d4e53ea71f3aa82a21f2ed434dd69b8c5767f11f2a4c24c8d2f517d2312c9e5248e5c69395fdca1c95a2b3286122c75f5783ddb6f53c - languageName: node - linkType: hard - -"@babel/traverse@npm:^7.21.5": - version: 7.21.5 - resolution: "@babel/traverse@npm:7.21.5" - dependencies: - "@babel/code-frame": ^7.21.4 - "@babel/generator": ^7.21.5 - "@babel/helper-environment-visitor": ^7.21.5 - "@babel/helper-function-name": ^7.21.0 - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.21.5 - "@babel/types": ^7.21.5 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: b403733fa7d858f0c8e224f0434a6ade641bc469a4f92975363391e796629d5bf53e544761dfe85039aab92d5389ebe7721edb309d7a5bb7df2bf74f37bf9f47 - languageName: node - linkType: hard - -"@babel/traverse@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/traverse@npm:7.22.5" +"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.22.5, @babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8": + version: 7.22.8 + resolution: "@babel/traverse@npm:7.22.8" dependencies: "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.5 + "@babel/generator": ^7.22.7 "@babel/helper-environment-visitor": ^7.22.5 "@babel/helper-function-name": ^7.22.5 "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.5 - "@babel/parser": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.22.7 "@babel/types": ^7.22.5 debug: ^4.1.0 globals: ^11.1.0 - checksum: 560931422dc1761f2df723778dcb4e51ce0d02e560cf2caa49822921578f49189a5a7d053b78a32dca33e59be886a6b2200a6e24d4ae9b5086ca0ba803815694 - languageName: node - linkType: hard - -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.19.0, @babel/types@npm:^7.2.0, @babel/types@npm:^7.21.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.21.5 - resolution: "@babel/types@npm:7.21.5" - dependencies: - "@babel/helper-string-parser": ^7.21.5 - "@babel/helper-validator-identifier": ^7.19.1 - to-fast-properties: ^2.0.0 - checksum: 43242a99c612d13285ee4af46cc0f1066bcb6ffd38307daef7a76e8c70f36cfc3255eb9e75c8e768b40e761176c313aec4d5c0b9d97a21e494d49d5fd123a9f7 - languageName: node - linkType: hard - -"@babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.21.4": - version: 7.21.4 - resolution: "@babel/types@npm:7.21.4" - dependencies: - "@babel/helper-string-parser": ^7.19.4 - "@babel/helper-validator-identifier": ^7.19.1 - to-fast-properties: ^2.0.0 - checksum: 587bc55a91ce003b0f8aa10d70070f8006560d7dc0360dc0406d306a2cb2a10154e2f9080b9c37abec76907a90b330a536406cb75e6bdc905484f37b75c73219 + checksum: a381369bc3eedfd13ed5fef7b884657f1c29024ea7388198149f0edc34bd69ce3966e9f40188d15f56490a5e12ba250ccc485f2882b53d41b054fccefb233e33 languageName: node linkType: hard -"@babel/types@npm:^7.22.5": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.7, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.22.5 resolution: "@babel/types@npm:7.22.5" dependencies: @@ -4649,13 +3330,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/android-arm64@npm:0.17.18" - checksum: ec47777acf96ffe5e36426e5c5715f74e154ddd2a4b2fcd12748250d7b3ded51c5a1a8a5f896f1524e52d3abf4b302aad0b2f30ac23b4efc41de2d01e359a34a - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm64@npm:0.17.19" @@ -4663,13 +3337,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/android-arm@npm:0.17.18" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - "@esbuild/android-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm@npm:0.17.19" @@ -4677,13 +3344,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/android-x64@npm:0.17.18" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - "@esbuild/android-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-x64@npm:0.17.19" @@ -4691,13 +3351,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/darwin-arm64@npm:0.17.18" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/darwin-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-arm64@npm:0.17.19" @@ -4705,13 +3358,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/darwin-x64@npm:0.17.18" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - "@esbuild/darwin-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-x64@npm:0.17.19" @@ -4719,13 +3365,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/freebsd-arm64@npm:0.17.18" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/freebsd-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-arm64@npm:0.17.19" @@ -4733,13 +3372,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/freebsd-x64@npm:0.17.18" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/freebsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-x64@npm:0.17.19" @@ -4747,13 +3379,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-arm64@npm:0.17.18" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/linux-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm64@npm:0.17.19" @@ -4761,13 +3386,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-arm@npm:0.17.18" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - "@esbuild/linux-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm@npm:0.17.19" @@ -4775,13 +3393,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-ia32@npm:0.17.18" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/linux-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ia32@npm:0.17.19" @@ -4789,13 +3400,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-loong64@npm:0.17.18" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - "@esbuild/linux-loong64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-loong64@npm:0.17.19" @@ -4803,13 +3407,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-mips64el@npm:0.17.18" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - "@esbuild/linux-mips64el@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-mips64el@npm:0.17.19" @@ -4817,13 +3414,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-ppc64@npm:0.17.18" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/linux-ppc64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ppc64@npm:0.17.19" @@ -4831,13 +3421,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-riscv64@npm:0.17.18" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - "@esbuild/linux-riscv64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-riscv64@npm:0.17.19" @@ -4845,13 +3428,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-s390x@npm:0.17.18" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - "@esbuild/linux-s390x@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-s390x@npm:0.17.19" @@ -4859,13 +3435,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/linux-x64@npm:0.17.18" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - "@esbuild/linux-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-x64@npm:0.17.19" @@ -4873,13 +3442,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/netbsd-x64@npm:0.17.18" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/netbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/netbsd-x64@npm:0.17.19" @@ -4887,13 +3449,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/openbsd-x64@npm:0.17.18" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/openbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/openbsd-x64@npm:0.17.19" @@ -4901,13 +3456,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/sunos-x64@npm:0.17.18" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - "@esbuild/sunos-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/sunos-x64@npm:0.17.19" @@ -4915,13 +3463,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/win32-arm64@npm:0.17.18" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/win32-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-arm64@npm:0.17.19" @@ -4929,13 +3470,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/win32-ia32@npm:0.17.18" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/win32-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-ia32@npm:0.17.19" @@ -4943,13 +3477,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.17.18": - version: 0.17.18 - resolution: "@esbuild/win32-x64@npm:0.17.18" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@esbuild/win32-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-x64@npm:0.17.19" @@ -4992,23 +3519,6 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.0.3": - version: 2.0.3 - resolution: "@eslint/eslintrc@npm:2.0.3" - dependencies: - ajv: ^6.12.4 - debug: ^4.3.2 - espree: ^9.5.2 - globals: ^13.19.0 - ignore: ^5.2.0 - import-fresh: ^3.2.1 - js-yaml: ^4.1.0 - minimatch: ^3.1.2 - strip-json-comments: ^3.1.1 - checksum: ddc51f25f8524d8231db9c9bf03177e503d941a332e8d5ce3b10b09241be4d5584a378a529a27a527586bfbccf3031ae539eb891352033c340b012b4d0c81d92 - languageName: node - linkType: hard - "@eslint/eslintrc@npm:^2.1.0": version: 2.1.0 resolution: "@eslint/eslintrc@npm:2.1.0" @@ -5026,13 +3536,6 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.43.0": - version: 8.43.0 - resolution: "@eslint/js@npm:8.43.0" - checksum: 580487a09c82ac169744d36e4af77bc4f582c9a37749d1e9481eb93626c8f3991b2390c6e4e69e5642e3b6e870912b839229a0e23594fae348156ea5a8ed7e2e - languageName: node - linkType: hard - "@eslint/js@npm:8.44.0": version: 8.44.0 resolution: "@eslint/js@npm:8.44.0" @@ -5197,15 +3700,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/date@npm:^3.0.1": - version: 3.0.1 - resolution: "@internationalized/date@npm:3.0.1" - dependencies: - "@babel/runtime": ^7.6.2 - checksum: ff51a00550322a5df3d3051e8ffdf3d7741851149e8ba300883e01402249602e87cc50b27b972753d9af88c5374df83c24adf58cae5e269100cb946a3b12cd56 - languageName: node - linkType: hard - "@internationalized/date@npm:^3.2.0": version: 3.2.0 resolution: "@internationalized/date@npm:3.2.0" @@ -5225,16 +3719,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/message@npm:^3.0.9": - version: 3.0.9 - resolution: "@internationalized/message@npm:3.0.9" - dependencies: - "@babel/runtime": ^7.6.2 - intl-messageformat: ^10.1.0 - checksum: b3f7f5a8e1d8df99efb3463ca07edb976ecf95d28de19a47d92fb19c093052b1a092aeaa226dc69d07143854bdbeb8519a0ac8ba8c900c4b0f565151d735ca7f - languageName: node - linkType: hard - "@internationalized/message@npm:^3.1.0": version: 3.1.0 resolution: "@internationalized/message@npm:3.1.0" @@ -5254,15 +3738,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/number@npm:^3.1.1": - version: 3.1.1 - resolution: "@internationalized/number@npm:3.1.1" - dependencies: - "@babel/runtime": ^7.6.2 - checksum: 9979ea1ca7388de75193c9d36f19d928fbcb715d456d153c30cafadd2ce1ceae011f55c966d424f4561ec04de14d3b48b8fe16a9e2737273829a813c4f7203a3 - languageName: node - linkType: hard - "@internationalized/number@npm:^3.2.0": version: 3.2.0 resolution: "@internationalized/number@npm:3.2.0" @@ -5281,15 +3756,6 @@ __metadata: languageName: node linkType: hard -"@internationalized/string@npm:^3.0.0": - version: 3.0.0 - resolution: "@internationalized/string@npm:3.0.0" - dependencies: - "@babel/runtime": ^7.6.2 - checksum: fc347cf80cd4ee009d1c467dca2c6908a919ad152086bf5e8c1a0aede0383fb317695fc5d82abe033ec90ad62108297130b653b63b9529f2e032999798ae4a81 - languageName: node - linkType: hard - "@internationalized/string@npm:^3.1.0": version: 3.1.0 resolution: "@internationalized/string@npm:3.1.0" @@ -5395,15 +3861,6 @@ __metadata: languageName: node linkType: hard -"@jest/expect-utils@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/expect-utils@npm:29.5.0" - dependencies: - jest-get-type: ^29.4.3 - checksum: c46fb677c88535cf83cf29f0a5b1f376c6a1109ddda266ad7da1a9cbc53cb441fa402dd61fc7b111ffc99603c11a9b3357ee41a1c0e035a58830bcb360871476 - languageName: node - linkType: hard - "@jest/expect-utils@npm:^29.6.1": version: 29.6.1 resolution: "@jest/expect-utils@npm:29.6.1" @@ -5495,15 +3952,6 @@ __metadata: languageName: node linkType: hard -"@jest/schemas@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/schemas@npm:29.4.3" - dependencies: - "@sinclair/typebox": ^0.25.16 - checksum: ac754e245c19dc39e10ebd41dce09040214c96a4cd8efa143b82148e383e45128f24599195ab4f01433adae4ccfbe2db6574c90db2862ccd8551a86704b5bebd - languageName: node - linkType: hard - "@jest/schemas@npm:^29.6.0": version: 29.6.0 resolution: "@jest/schemas@npm:29.6.0" @@ -5571,29 +4019,6 @@ __metadata: languageName: node linkType: hard -"@jest/transform@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/transform@npm:29.5.0" - dependencies: - "@babel/core": ^7.11.6 - "@jest/types": ^29.5.0 - "@jridgewell/trace-mapping": ^0.3.15 - babel-plugin-istanbul: ^6.1.1 - chalk: ^4.0.0 - convert-source-map: ^2.0.0 - fast-json-stable-stringify: ^2.1.0 - graceful-fs: ^4.2.9 - jest-haste-map: ^29.5.0 - jest-regex-util: ^29.4.3 - jest-util: ^29.5.0 - micromatch: ^4.0.4 - pirates: ^4.0.4 - slash: ^3.0.0 - write-file-atomic: ^4.0.2 - checksum: d55d604085c157cf5112e165ff5ac1fa788873b3b31265fb4734ca59892ee24e44119964cc47eb6d178dd9512bbb6c576d1e20e51a201ff4e24d31e818a1c92d - languageName: node - linkType: hard - "@jest/transform@npm:^29.6.1": version: 29.6.1 resolution: "@jest/transform@npm:29.6.1" @@ -5643,20 +4068,6 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:^29.5.0": - version: 29.5.0 - resolution: "@jest/types@npm:29.5.0" - dependencies: - "@jest/schemas": ^29.4.3 - "@types/istanbul-lib-coverage": ^2.0.0 - "@types/istanbul-reports": ^3.0.0 - "@types/node": "*" - "@types/yargs": ^17.0.8 - chalk: ^4.0.0 - checksum: 1811f94b19cf8a9460a289c4f056796cfc373480e0492692a6125a553cd1a63824bd846d7bb78820b7b6f758f6dd3c2d4558293bb676d541b2fa59c70fdf9d39 - languageName: node - linkType: hard - "@jest/types@npm:^29.6.1": version: 29.6.1 resolution: "@jest/types@npm:29.6.1" @@ -5682,20 +4093,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0": +"@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": version: 3.1.0 resolution: "@jridgewell/resolve-uri@npm:3.1.0" checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.0.5 - resolution: "@jridgewell/resolve-uri@npm:3.0.5" - checksum: 1ee652b693da7979ac4007926cc3f0a32b657ffeb913e111f44e5b67153d94a2f28a1d560101cc0cf8087625468293a69a00f634a2914e1a6d0817ba2039a913 - languageName: node - linkType: hard - "@jridgewell/set-array@npm:^1.0.1": version: 1.1.2 resolution: "@jridgewell/set-array@npm:1.1.2" @@ -5703,16 +4107,6 @@ __metadata: languageName: node linkType: hard -"@jridgewell/source-map@npm:^0.3.2": - version: 0.3.2 - resolution: "@jridgewell/source-map@npm:0.3.2" - dependencies: - "@jridgewell/gen-mapping": ^0.3.0 - "@jridgewell/trace-mapping": ^0.3.9 - checksum: 1b83f0eb944e77b70559a394d5d3b3f98a81fcc186946aceb3ef42d036762b52ef71493c6c0a3b7c1d2f08785f53ba2df1277fe629a06e6109588ff4cdcf7482 - languageName: node - linkType: hard - "@jridgewell/source-map@npm:^0.3.3": version: 0.3.3 resolution: "@jridgewell/source-map@npm:0.3.3" @@ -5723,20 +4117,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14": +"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10": version: 1.4.14 resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.11 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.11" - checksum: 3b2afaf8400fb07a36db60e901fcce6a746cdec587310ee9035939d89878e57b2dec8173b0b8f63176f647efa352294049a53c49739098eb907ff81fec2547c8 - languageName: node - linkType: hard - "@jridgewell/trace-mapping@npm:0.3.9": version: 0.3.9 resolution: "@jridgewell/trace-mapping@npm:0.3.9" @@ -5747,7 +4134,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.14, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.18 resolution: "@jridgewell/trace-mapping@npm:0.3.18" dependencies: @@ -5795,16 +4182,7 @@ __metadata: languageName: node linkType: hard -"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3": - version: 1.1.5 - resolution: "@lezer/highlight@npm:1.1.5" - dependencies: - "@lezer/common": ^1.0.0 - checksum: 1f0b0a3dc7e1f23d889ce7a61d9ce1ba4d3b307205baf58f97252588df4c6751e4c86d39c20cd0bc7ac39ab82ff78a9251db91148b3b069965ec55d5fe9c4ef5 - languageName: node - linkType: hard - -"@lezer/highlight@npm:^1.1.6": +"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3, @lezer/highlight@npm:^1.1.6": version: 1.1.6 resolution: "@lezer/highlight@npm:1.1.6" dependencies: @@ -6960,24 +5338,6 @@ __metadata: languageName: node linkType: hard -"@react-aria/i18n@npm:^3.6.0": - version: 3.6.0 - resolution: "@react-aria/i18n@npm:3.6.0" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/date": ^3.0.1 - "@internationalized/message": ^3.0.9 - "@internationalized/number": ^3.1.1 - "@internationalized/string": ^3.0.0 - "@react-aria/ssr": ^3.3.0 - "@react-aria/utils": ^3.13.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: ede9cd611e15fe2975556dfe695bdcb67cbcb8d2dfff7677174f86f1418421491fbbbfd8eab40e724a8db24877d2f980df6e50d26d29d5b3e607ca39b42befc3 - languageName: node - linkType: hard - "@react-aria/i18n@npm:^3.7.0, @react-aria/i18n@npm:^3.7.1": version: 3.7.1 resolution: "@react-aria/i18n@npm:3.7.1" @@ -7383,17 +5743,6 @@ __metadata: languageName: node linkType: hard -"@react-aria/ssr@npm:^3.3.0": - version: 3.3.0 - resolution: "@react-aria/ssr@npm:3.3.0" - dependencies: - "@babel/runtime": ^7.6.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 0b7677ef521c65452460601dce3c264b67baa75ef7c99e9755ea55913765054156b6157c9c42e3d56aba86d1704b8b2aeb7672e4084f2f375fe1ec481e33c8c6 - languageName: node - linkType: hard - "@react-aria/ssr@npm:^3.5.0, @react-aria/ssr@npm:^3.6.0": version: 3.6.0 resolution: "@react-aria/ssr@npm:3.6.0" @@ -7548,21 +5897,6 @@ __metadata: languageName: node linkType: hard -"@react-aria/utils@npm:^3.13.3": - version: 3.13.3 - resolution: "@react-aria/utils@npm:3.13.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-aria/ssr": ^3.3.0 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - clsx: ^1.1.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: b6d87ddb8e1d93b00405473099390c854647d81c0419de53cc4a7f02bdcca6d030776fba9f4b241400af13082bafc820dd5ce05c168e8f5a2c43a1b2660fb2ad - languageName: node - linkType: hard - "@react-aria/utils@npm:^3.15.0, @react-aria/utils@npm:^3.16.0": version: 3.16.0 resolution: "@react-aria/utils@npm:3.16.0" @@ -7841,23 +6175,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/calendar@npm:^3.0.2": - version: 3.0.2 - resolution: "@react-stately/calendar@npm:3.0.2" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/date": ^3.0.1 - "@react-stately/utils": ^3.5.1 - "@react-types/calendar": ^3.0.2 - "@react-types/datepicker": ^3.1.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: c093cab8761b1e16603abcde63f78dfefdb7fdf4cc269e41602ab3a7c93f9391d29ac68cc66e030c553305af7d96ff9afa3795123211a59316819937a8181956 - languageName: node - linkType: hard - -"@react-stately/calendar@npm:^3.2.0": +"@react-stately/calendar@npm:^3.0.2, @react-stately/calendar@npm:^3.2.0": version: 3.2.0 resolution: "@react-stately/calendar@npm:3.2.0" dependencies: @@ -7873,21 +6191,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/checkbox@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/checkbox@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/toggle": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/checkbox": ^3.3.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 9035b595fa21cc1bef7e04249ec9df2293e93310dd644e4d32087ce19bd77aae38db3e676f6fdffbde875bc9a318f05dd60c61ab6e0d9b524222438e7ef31cd7 - languageName: node - linkType: hard - -"@react-stately/checkbox@npm:^3.4.1": +"@react-stately/checkbox@npm:^3.2.1, @react-stately/checkbox@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/checkbox@npm:3.4.1" dependencies: @@ -7902,19 +6206,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/collections@npm:^3.4.3": - version: 3.4.3 - resolution: "@react-stately/collections@npm:3.4.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f9045cdac0b20f7d7464ac37c0402511f7c5a727676d0cfefef74a553247d0dd1c816ea5804aac318d85ea5708599f9c9c2e8bd37165b5c6eec100e27f3832b9 - languageName: node - linkType: hard - -"@react-stately/collections@npm:^3.7.0": +"@react-stately/collections@npm:^3.4.3, @react-stately/collections@npm:^3.7.0": version: 3.7.0 resolution: "@react-stately/collections@npm:3.7.0" dependencies: @@ -7944,24 +6236,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/combobox@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/combobox@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/list": ^3.5.3 - "@react-stately/menu": ^3.4.1 - "@react-stately/select": ^3.3.1 - "@react-stately/utils": ^3.5.1 - "@react-types/combobox": ^3.5.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3e9a9050e8e20c96ae703876e652d28d2e3cf9dca79008d8e0f9fd096e88f74215add97e7d4aec9fe93afd64ebd676e5593d5178a28ad76c180207740fc47712 - languageName: node - linkType: hard - -"@react-stately/combobox@npm:^3.5.0": +"@react-stately/combobox@npm:^3.2.1, @react-stately/combobox@npm:^3.5.0": version: 3.5.0 resolution: "@react-stately/combobox@npm:3.5.0" dependencies: @@ -7991,24 +6266,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/datepicker@npm:^3.0.2": - version: 3.0.2 - resolution: "@react-stately/datepicker@npm:3.0.2" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/date": ^3.0.1 - "@internationalized/string": ^3.0.0 - "@react-stately/overlays": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/datepicker": ^3.1.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: d0250033d8f4625442177eac1ced6fe446877df9607bd1d7bdea11daae47166072304ee66d4ce1fe12886ef24c0cc1983ac5807a1fe07b05a5749d6b8302f47b - languageName: node - linkType: hard - -"@react-stately/datepicker@npm:^3.4.0": +"@react-stately/datepicker@npm:^3.0.2, @react-stately/datepicker@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/datepicker@npm:3.4.0" dependencies: @@ -8038,20 +6296,6 @@ __metadata: languageName: node linkType: hard -"@react-stately/grid@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-stately/grid@npm:3.3.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/selection": ^3.10.3 - "@react-types/grid": ^3.1.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 84e1f24d2dcac51b1ab99f0ad403c965eb9988fa236054a5c137efb1917a455d56a1b78f820a77c3af38895d60a24884cfeac5a482b36390b629612ee8c7e7f3 - languageName: node - linkType: hard - "@react-stately/grid@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/grid@npm:3.6.0" @@ -8084,22 +6328,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/list@npm:^3.5.3": - version: 3.5.3 - resolution: "@react-stately/list@npm:3.5.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/selection": ^3.10.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 162ba719db06a1649bbeb655c78e8a3f3c17a4c02f3318479ce2cc71940052f4a3cc98e67fd604f48ed89f199c731fb6d7c4d6e7b36d53593a0fc9b38d5e465c - languageName: node - linkType: hard - -"@react-stately/list@npm:^3.8.0": +"@react-stately/list@npm:^3.5.3, @react-stately/list@npm:^3.8.0": version: 3.8.0 resolution: "@react-stately/list@npm:3.8.0" dependencies: @@ -8114,22 +6343,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/menu@npm:^3.4.1": - version: 3.4.1 - resolution: "@react-stately/menu@npm:3.4.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/overlays": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/menu": ^3.7.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: a944d6e3a3caf400ffc52738ee8d586db6c6846d0ecc009de4bbedc88202f63d6bbddbd3d577f730f98f28404b077676af4c307f4ba09314c79cf56087a5aa8c - languageName: node - linkType: hard - -"@react-stately/menu@npm:^3.5.1": +"@react-stately/menu@npm:^3.4.1, @react-stately/menu@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/menu@npm:3.5.1" dependencies: @@ -8144,22 +6358,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/numberfield@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/numberfield@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@internationalized/number": ^3.1.1 - "@react-stately/utils": ^3.5.1 - "@react-types/numberfield": ^3.3.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 5698d237c8fbe65cc7ab85c586ffadd92d085f15cab542003419deeccc2f13f2aa839dc844df8853648d892d6580fd4dd15a0b0d4eba86a467afbdb8d3c1675f - languageName: node - linkType: hard - -"@react-stately/numberfield@npm:^3.4.1": +"@react-stately/numberfield@npm:^3.2.1, @react-stately/numberfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/numberfield@npm:3.4.1" dependencies: @@ -8174,20 +6373,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/overlays@npm:^3.4.1": - version: 3.4.1 - resolution: "@react-stately/overlays@npm:3.4.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/overlays": ^3.6.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3e0e8711c55198b75cb23a682530969c997fdd21c280a9a1356327ff3806252a70ef13e4efc7734902edfd58d6c2cc9d2624a37d8394ad44e9d33b09186510e3 - languageName: node - linkType: hard - -"@react-stately/overlays@npm:^3.5.1": +"@react-stately/overlays@npm:^3.4.1, @react-stately/overlays@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/overlays@npm:3.5.1" dependencies: @@ -8214,20 +6400,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/radio@npm:^3.5.1": - version: 3.5.1 - resolution: "@react-stately/radio@npm:3.5.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/radio": ^3.2.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 7a60de8afb5d8ccaf33da66613ae55a4b2eca75bacae902574282c33ab66684b1ae5db95b2743fdcc926d1c0464af7e6d837f6a5b85bb00836a9c78ba65c3623 - languageName: node - linkType: hard - -"@react-stately/radio@npm:^3.8.0": +"@react-stately/radio@npm:^3.5.1, @react-stately/radio@npm:^3.8.0": version: 3.8.0 resolution: "@react-stately/radio@npm:3.8.0" dependencies: @@ -8241,21 +6414,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/searchfield@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-stately/searchfield@npm:3.3.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/searchfield": ^3.3.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f52776a294450382ea9f2abacea6b2972ea4f96ef6ffaff33c62f783881ceb74cd6aec959178499d6c7acf49b3a671d1902f0eb9cc4f2dba486ca88f7514693b - languageName: node - linkType: hard - -"@react-stately/searchfield@npm:^3.4.1": +"@react-stately/searchfield@npm:^3.3.1, @react-stately/searchfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-stately/searchfield@npm:3.4.1" dependencies: @@ -8269,25 +6428,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/select@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-stately/select@npm:3.3.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/list": ^3.5.3 - "@react-stately/menu": ^3.4.1 - "@react-stately/selection": ^3.10.3 - "@react-stately/utils": ^3.5.1 - "@react-types/select": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 0701cadd640fdea8a3a1c7048e459f701fc8ec9c0ef1fb9692fd70faa5bb7ce23475aba988f57dff90a3db71cbbf8b1ba49edc3df43e744550fbd0e2dcc3575f - languageName: node - linkType: hard - -"@react-stately/select@npm:^3.5.0": +"@react-stately/select@npm:^3.3.1, @react-stately/select@npm:^3.5.0": version: 3.5.0 resolution: "@react-stately/select@npm:3.5.0" dependencies: @@ -8305,21 +6446,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/selection@npm:^3.10.3": - version: 3.10.3 - resolution: "@react-stately/selection@npm:3.10.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f65af198fa9199bc6bcf76279e2131b605e3ce449cc61d404de34993c81f499d0aba34916e8e8fd867d01ae60786ea3c3b725f3c73153674812bf29e64c6a531 - languageName: node - linkType: hard - -"@react-stately/selection@npm:^3.13.0": +"@react-stately/selection@npm:^3.10.3, @react-stately/selection@npm:^3.13.0": version: 3.13.0 resolution: "@react-stately/selection@npm:3.13.0" dependencies: @@ -8349,23 +6476,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/slider@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/slider@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-aria/i18n": ^3.6.0 - "@react-aria/utils": ^3.13.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - "@react-types/slider": ^3.2.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3d20eae41b79e481fc45cb4671b17ea20010f199790c963766a58067df18c1b83b41b1394ff3b053b32306cd952bad12331dec09c2a6a6c0c060f336aafee0ca - languageName: node - linkType: hard - -"@react-stately/slider@npm:^3.3.1": +"@react-stately/slider@npm:^3.2.1, @react-stately/slider@npm:^3.3.1": version: 3.3.1 resolution: "@react-stately/slider@npm:3.3.1" dependencies: @@ -8381,24 +6492,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/table@npm:^3.4.0": - version: 3.4.0 - resolution: "@react-stately/table@npm:3.4.0" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/grid": ^3.3.1 - "@react-stately/selection": ^3.10.3 - "@react-types/grid": ^3.1.3 - "@react-types/shared": ^3.14.1 - "@react-types/table": ^3.3.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f3571875fe9978d1f99554d8a31b3af3ced6ac84fde77ed175620f5ce76952833b98ee41b383f5098489c41f504942cedcffe604a4ec6158bbe320267eb70d01 - languageName: node - linkType: hard - -"@react-stately/table@npm:^3.9.0": +"@react-stately/table@npm:^3.4.0, @react-stately/table@npm:^3.9.0": version: 3.9.0 resolution: "@react-stately/table@npm:3.9.0" dependencies: @@ -8415,21 +6509,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/tabs@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/tabs@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/list": ^3.5.3 - "@react-stately/utils": ^3.5.1 - "@react-types/tabs": ^3.1.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 593d4ea004ed89156ebf6e2eea401d30e4b06e9eae0f83752550bc5d3d776008577ba0e6baca9791c2e1c0af0f15881a8f95f18923132af08de172cecf097d20 - languageName: node - linkType: hard - -"@react-stately/tabs@npm:^3.4.0": +"@react-stately/tabs@npm:^3.2.1, @react-stately/tabs@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/tabs@npm:3.4.0" dependencies: @@ -8444,21 +6524,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/toggle@npm:^3.4.1": - version: 3.4.1 - resolution: "@react-stately/toggle@npm:3.4.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/utils": ^3.5.1 - "@react-types/checkbox": ^3.3.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 6cc297ac5c840aa20a6d304947a4d869b857c9dc522b7e77cf798f1815ebd5e5ae1f00aeb812fa452fbbfada1069e814b9e1aaf2751b747f875f8b88d88c21fe - languageName: node - linkType: hard - -"@react-stately/toggle@npm:^3.5.1": +"@react-stately/toggle@npm:^3.4.1, @react-stately/toggle@npm:^3.5.1": version: 3.5.1 resolution: "@react-stately/toggle@npm:3.5.1" dependencies: @@ -8472,21 +6538,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/tooltip@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-stately/tooltip@npm:3.2.1" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/overlays": ^3.4.1 - "@react-stately/utils": ^3.5.1 - "@react-types/tooltip": ^3.2.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: dbb650986c11284dc45b6c0940e3a5aecb7d5e1af92828ae93b4ec1441b580461340033f427523b16f216afb815ebdc491f7aae361e5cd3bcc3dcea1268c76ab - languageName: node - linkType: hard - -"@react-stately/tooltip@npm:^3.4.0": +"@react-stately/tooltip@npm:^3.2.1, @react-stately/tooltip@npm:^3.4.0": version: 3.4.0 resolution: "@react-stately/tooltip@npm:3.4.0" dependencies: @@ -8500,22 +6552,7 @@ __metadata: languageName: node linkType: hard -"@react-stately/tree@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-stately/tree@npm:3.3.3" - dependencies: - "@babel/runtime": ^7.6.2 - "@react-stately/collections": ^3.4.3 - "@react-stately/selection": ^3.10.3 - "@react-stately/utils": ^3.5.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 4e1a94cb478124a2443e84dbf0160dd3a5298e79478336f07003b8c5fcdb26043c65a94439a17315cf00e7f66bf6fd5e3e6fbcb44bced3352554d8f7be94899a - languageName: node - linkType: hard - -"@react-stately/tree@npm:^3.6.0": +"@react-stately/tree@npm:^3.3.3, @react-stately/tree@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/tree@npm:3.6.0" dependencies: @@ -8541,17 +6578,6 @@ __metadata: languageName: node linkType: hard -"@react-stately/utils@npm:^3.5.1": - version: 3.5.1 - resolution: "@react-stately/utils@npm:3.5.1" - dependencies: - "@babel/runtime": ^7.6.2 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f748331ae393f97b3e6fcccd37b767358f49229520b9500f82ed4c620bff36ef3c01d4ba9679ac7b9d6d78c5f6e711186c98bd0e6482ec27a6fbf26c5d0aa3cc - languageName: node - linkType: hard - "@react-stately/utils@npm:^3.6.0": version: 3.6.0 resolution: "@react-stately/utils@npm:3.6.0" @@ -8610,18 +6636,6 @@ __metadata: languageName: node linkType: hard -"@react-types/calendar@npm:^3.0.2": - version: 3.0.2 - resolution: "@react-types/calendar@npm:3.0.2" - dependencies: - "@internationalized/date": ^3.0.1 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: a3fd271d85064837c3b7a495e4048c25da1bbbc21015cfadd970f9959e8c802c9152e25ea772ffd815655e392ce07ce75c688726e70bb4cf6959605bc8257c8e - languageName: node - linkType: hard - "@react-types/calendar@npm:^3.2.0": version: 3.2.0 resolution: "@react-types/calendar@npm:3.2.0" @@ -8634,17 +6648,6 @@ __metadata: languageName: node linkType: hard -"@react-types/checkbox@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-types/checkbox@npm:3.3.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: d1da491ff3bf14f894dbeab5ace3a397ead306d2cc4a820d2a653e038a5628495417feb10a4e07c05dcfce208ae9303c35de7e57d1b21a6b59ca1acca11b80d8 - languageName: node - linkType: hard - "@react-types/checkbox@npm:^3.4.3": version: 3.4.3 resolution: "@react-types/checkbox@npm:3.4.3" @@ -8668,17 +6671,6 @@ __metadata: languageName: node linkType: hard -"@react-types/combobox@npm:^3.5.3": - version: 3.5.3 - resolution: "@react-types/combobox@npm:3.5.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 41e1371f1efa48fe4d56afffeca59d1ed9dad75565c3d67fdf9f6c594529113ce9a8053b95d419682364878a6df0fd6a7178c20e6735778eea2abe74de1ca24f - languageName: node - linkType: hard - "@react-types/combobox@npm:^3.6.1": version: 3.6.1 resolution: "@react-types/combobox@npm:3.6.1" @@ -8690,19 +6682,6 @@ __metadata: languageName: node linkType: hard -"@react-types/datepicker@npm:^3.1.1": - version: 3.1.1 - resolution: "@react-types/datepicker@npm:3.1.1" - dependencies: - "@internationalized/date": ^3.0.1 - "@react-types/overlays": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: a3ab8ae22da8105ffebdebb5c89f212cda4d6f2203f7579cbd733e36afb4d2c7e4f986082becacaa66e7d1b4a99ef109952ddae7760d4bb0685e71d53894e316 - languageName: node - linkType: hard - "@react-types/datepicker@npm:^3.3.0": version: 3.3.0 resolution: "@react-types/datepicker@npm:3.3.0" @@ -8728,17 +6707,6 @@ __metadata: languageName: node linkType: hard -"@react-types/grid@npm:^3.1.3": - version: 3.1.3 - resolution: "@react-types/grid@npm:3.1.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 124b366436160ac7b88368a8be37abf4c703bde3fc1275e720f76d9ee8d0a10825fc5dd314b5eb6bb17b7d0c87091608d9b96d9521329ee5baeb94ab08fa3835 - languageName: node - linkType: hard - "@react-types/grid@npm:^3.1.7": version: 3.1.7 resolution: "@react-types/grid@npm:3.1.7" @@ -8795,18 +6763,6 @@ __metadata: languageName: node linkType: hard -"@react-types/menu@npm:^3.7.1": - version: 3.7.1 - resolution: "@react-types/menu@npm:3.7.1" - dependencies: - "@react-types/overlays": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 349443d1bd23bf64a9af57bef57d8ebfebfc6e82dbcef5cfd8ba778afc998f8dc3cebaae80728e6017b0d12b9e5aaea783254df36dd1b82a048b9e3c0e095795 - languageName: node - linkType: hard - "@react-types/menu@npm:^3.9.0": version: 3.9.0 resolution: "@react-types/menu@npm:3.9.0" @@ -8842,17 +6798,6 @@ __metadata: languageName: node linkType: hard -"@react-types/numberfield@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-types/numberfield@npm:3.3.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: b0f6627157dea0ce8a8fa3434c55bbbc69c4b46c84024d6da6f97091eae45c8b4f9b5b842c2ff82712c1b2e407b4acbd0d476ceda25abd3b9402a0b4573b3b52 - languageName: node - linkType: hard - "@react-types/numberfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/numberfield@npm:3.4.1" @@ -8864,17 +6809,6 @@ __metadata: languageName: node linkType: hard -"@react-types/overlays@npm:^3.6.3": - version: 3.6.3 - resolution: "@react-types/overlays@npm:3.6.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 8688db82adeda13e922f9805a5c9bd9f64e97e91c0ebf32409964e9d661828a4bb31907551dcdcd611807efa9824ff78aa8cb2ee4b0acfab001cbff5572336d4 - languageName: node - linkType: hard - "@react-types/overlays@npm:^3.7.1": version: 3.7.1 resolution: "@react-types/overlays@npm:3.7.1" @@ -8908,17 +6842,6 @@ __metadata: languageName: node linkType: hard -"@react-types/radio@npm:^3.2.3": - version: 3.2.3 - resolution: "@react-types/radio@npm:3.2.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: ce37d92a7e6665a9900b232aae68978bf1c82b4dffd30cc896c6382df7a9bb8a501a30f1b819d0630f9cbf21af3cb6f51de05fbaeaef4a1f250e5d39276eba59 - languageName: node - linkType: hard - "@react-types/radio@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/radio@npm:3.4.1" @@ -8930,18 +6853,6 @@ __metadata: languageName: node linkType: hard -"@react-types/searchfield@npm:^3.3.3": - version: 3.3.3 - resolution: "@react-types/searchfield@npm:3.3.3" - dependencies: - "@react-types/shared": ^3.14.1 - "@react-types/textfield": ^3.5.3 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: cee59f6ad1da98cc01b81252ef91ebddf0a46df73e4cb3016474c9ad288a0d7b3de2d4607285de97ec23ecaba50391980268ac69b2def096c1fe3a33ecffc686 - languageName: node - linkType: hard - "@react-types/searchfield@npm:^3.4.1": version: 3.4.1 resolution: "@react-types/searchfield@npm:3.4.1" @@ -8954,17 +6865,6 @@ __metadata: languageName: node linkType: hard -"@react-types/select@npm:^3.6.3": - version: 3.6.3 - resolution: "@react-types/select@npm:3.6.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 472d3086e13ca18857659c5a93e36d5e00c4f1077fd627b16ed93641e6ec39aed77a6cb819e3115616486df3178c2e725aef8dd95cd36fc297819b78111d10b8 - languageName: node - linkType: hard - "@react-types/select@npm:^3.8.0": version: 3.8.0 resolution: "@react-types/select@npm:3.8.0" @@ -8985,16 +6885,7 @@ __metadata: languageName: node linkType: hard -"@react-types/shared@npm:^3.14.1": - version: 3.14.1 - resolution: "@react-types/shared@npm:3.14.1" - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 117fe230f5a26b7fcaf535c1cfb7c4d42416b0f49d0e0b3436fef2a5851234967908c4e884fc5f2a99a04bee2543543348346a04e1f3f45aaa14c42b6f08491a - languageName: node - linkType: hard - -"@react-types/shared@npm:^3.18.0": +"@react-types/shared@npm:^3.14.1, @react-types/shared@npm:^3.18.0": version: 3.18.0 resolution: "@react-types/shared@npm:3.18.0" peerDependencies: @@ -9014,17 +6905,6 @@ __metadata: languageName: node linkType: hard -"@react-types/slider@npm:^3.2.1": - version: 3.2.1 - resolution: "@react-types/slider@npm:3.2.1" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 3c64ab2d99fd14debd74181ab4faef43656b274f00443899564e89f3b4b8d9c327184a9c236e4f69c4efc8cba0eca0a0aeae686dcf9a521a7749bab4e0bbdfbb - languageName: node - linkType: hard - "@react-types/slider@npm:^3.5.0": version: 3.5.0 resolution: "@react-types/slider@npm:3.5.0" @@ -9048,18 +6928,6 @@ __metadata: languageName: node linkType: hard -"@react-types/table@npm:^3.3.1": - version: 3.3.1 - resolution: "@react-types/table@npm:3.3.1" - dependencies: - "@react-types/grid": ^3.1.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 1d3e4f8bac6e886944f67c159224893e63ec500f18aaadc74613d9053382c53fd282a7ee9dc21616b7fc0e1291d6ec7ccae87ebca2abdd19e8b371fb8cb46abc - languageName: node - linkType: hard - "@react-types/table@npm:^3.6.0": version: 3.6.0 resolution: "@react-types/table@npm:3.6.0" @@ -9072,17 +6940,6 @@ __metadata: languageName: node linkType: hard -"@react-types/tabs@npm:^3.1.3": - version: 3.1.3 - resolution: "@react-types/tabs@npm:3.1.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 04a95bfb92d2fe44900135bbdd1d256622c51fc90ebecc3374d29eb69bbb77ec13156d39b9fe1806e66b726cf5bbe9ff64822e04e5f3bcacd2429e27c3c260e1 - languageName: node - linkType: hard - "@react-types/tabs@npm:^3.2.1": version: 3.2.1 resolution: "@react-types/tabs@npm:3.2.1" @@ -9105,17 +6962,6 @@ __metadata: languageName: node linkType: hard -"@react-types/textfield@npm:^3.5.3": - version: 3.5.3 - resolution: "@react-types/textfield@npm:3.5.3" - dependencies: - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: f684821edba64e0b525606590800bf2cb6aea98c7304956ed3b2bbcb129ba7734897a9ca1bd056c2f23bf515399fed654071de6a2037942093c2af1c07fad1a9 - languageName: node - linkType: hard - "@react-types/textfield@npm:^3.7.1": version: 3.7.1 resolution: "@react-types/textfield@npm:3.7.1" @@ -9127,18 +6973,6 @@ __metadata: languageName: node linkType: hard -"@react-types/tooltip@npm:^3.2.3": - version: 3.2.3 - resolution: "@react-types/tooltip@npm:3.2.3" - dependencies: - "@react-types/overlays": ^3.6.3 - "@react-types/shared": ^3.14.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 - checksum: 5079ee2e561c2b9a7cc6e9dd22d48a26b0d61d79114aff730b7fc8348e199ec1db9e2ef96725f1682ebd5e93bbb223b920aa507f0f9997d8bc3df76f315077a6 - languageName: node - linkType: hard - "@react-types/tooltip@npm:^3.4.0": version: 3.4.0 resolution: "@react-types/tooltip@npm:3.4.0" @@ -9227,12 +7061,11 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": next "@types/bcrypt": ^5.0.0 - "@types/eslint": ~8.40.2 "@types/node": ^14.18.51 "@types/polka": ^0.5.4 bcrypt: ^5.0.1 ejson: ^2.2.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 mem: ^8.1.1 @@ -9242,7 +7075,7 @@ __metadata: pino: ^8.4.2 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 uuid: ^9.0.0 languageName: unknown linkType: soft @@ -9252,10 +7085,10 @@ __metadata: resolution: "@rocket.chat/account-utils@workspace:packages/account-utils" dependencies: "@types/jest": ~29.5.3 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9268,13 +7101,13 @@ __metadata: cron: ~1.8.2 date.js: ~0.3.3 debug: ~4.1.1 - eslint: ~8.43.0 + eslint: ~8.45.0 human-interval: ^2.0.1 jest: ~29.6.1 moment-timezone: ~0.5.43 mongodb: ^4.12.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9288,7 +7121,7 @@ __metadata: "@swc/jest": ^0.2.26 "@types/jest": ~29.5.3 "@types/strict-uri-encode": ^2.0.0 - eslint: ~8.43.0 + eslint: ~8.45.0 filter-obj: ^3.0.0 jest: ~29.6.1 jest-fetch-mock: ^3.0.3 @@ -9296,7 +7129,7 @@ __metadata: split-on-first: ^3.0.0 strict-uri-encode: ^2.0.0 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9348,11 +7181,10 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": next - "@types/eslint": ~8.40.2 "@types/node": ^14.18.51 "@types/polka": ^0.5.4 ejson: ^2.2.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 mem: ^8.1.1 @@ -9362,7 +7194,7 @@ __metadata: pino: ^8.4.2 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9370,15 +7202,15 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/base64@workspace:packages/base64" dependencies: - "@babel/core": ~7.22.5 - "@babel/preset-env": ~7.22.5 + "@babel/core": ~7.22.9 + "@babel/preset-env": ~7.22.9 "@rocket.chat/eslint-config": "workspace:^" - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 - eslint: ~8.43.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9388,10 +7220,10 @@ __metadata: dependencies: "@types/jest": ~29.5.3 cheerio: 1.0.0-rc.10 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9399,9 +7231,9 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/core-services@workspace:packages/core-services" dependencies: - "@babel/core": ^7.21.4 - "@babel/preset-env": ^7.21.4 - "@babel/preset-typescript": ^7.21.4 + "@babel/core": ~7.22.9 + "@babel/preset-env": ~7.22.9 + "@babel/preset-typescript": ~7.22.5 "@rocket.chat/apps-engine": 1.39.1 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" @@ -9415,12 +7247,12 @@ __metadata: "@types/fibers": ^3.1.1 "@types/jest": ~29.5.3 babel-jest: ^29.5.0 - eslint: ~8.43.0 + eslint: ~8.45.0 fibers: ^5.0.3 jest: ~29.6.1 mongodb: ^4.12.1 prettier: ~2.8.8 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9433,10 +7265,10 @@ __metadata: "@rocket.chat/icons": next "@rocket.chat/message-parser": next "@rocket.chat/ui-kit": next - eslint: ~8.43.0 + eslint: ~8.45.0 mongodb: ^4.12.1 prettier: ~2.8.8 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9449,11 +7281,11 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/random": "workspace:^" "@types/jest": ~29.5.3 - eslint: ^8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9502,11 +7334,11 @@ __metadata: "@swc/jest": ^0.2.26 "@types/jest": ~29.5.3 "@types/ws": ^8.5.5 - eslint: ^8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 jest-environment-jsdom: ~29.6.1 jest-websocket-mock: ^2.4.0 - typescript: ~5.1.3 + typescript: ~5.1.6 ws: ^8.13.0 peerDependencies: "@rocket.chat/emitter": "*" @@ -9529,7 +7361,6 @@ __metadata: "@rocket.chat/string-helpers": next "@rocket.chat/ui-contexts": "workspace:^" "@types/ejson": ^2.2.0 - "@types/eslint": ~8.40.2 "@types/meteor": ^2.9.2 "@types/node": ^14.18.51 "@types/polka": ^0.5.4 @@ -9538,7 +7369,7 @@ __metadata: "@types/ws": ^8.5.5 colorette: ^1.4.0 ejson: ^2.2.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 jaeger-client: ^3.19.0 @@ -9551,7 +7382,7 @@ __metadata: polka: ^0.5.2 sharp: ^0.30.7 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 underscore: ^1.13.6 uuid: ^7.0.3 ws: ^8.8.1 @@ -9569,11 +7400,12 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/eslint-config@workspace:packages/eslint-config" dependencies: - "@babel/eslint-parser": ~7.22.5 + "@babel/eslint-parser": ~7.22.9 + "@types/eslint": ~8.44.0 "@types/prettier": ^2.6.3 - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 - eslint: ~8.43.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 + eslint: ~8.45.0 eslint-config-prettier: ~8.8.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-import: ~2.26.0 @@ -9587,8 +7419,8 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/favicon@workspace:packages/favicon" dependencies: - eslint: ~8.43.0 - typescript: ~5.1.3 + eslint: ~8.45.0 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9734,7 +7566,7 @@ __metadata: "@types/react-dom": ~17.0.20 babel-loader: ~8.2.5 cross-env: ^7.0.3 - eslint: ~8.43.0 + eslint: ~8.45.0 normalize.css: ^8.0.1 npm-run-all: ^4.1.5 prettier: ~2.8.8 @@ -9742,7 +7574,7 @@ __metadata: react-dom: ^17.0.2 rimraf: ^3.0.2 tslib: ^2.5.3 - typescript: ~5.1.3 + typescript: ~5.1.6 peerDependencies: "@rocket.chat/apps-engine": "*" "@rocket.chat/eslint-config": "*" @@ -9789,7 +7621,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/gazzodown@workspace:packages/gazzodown" dependencies: - "@babel/core": ~7.22.5 + "@babel/core": ~7.22.9 "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/css-in-js": next "@rocket.chat/fuselage": next @@ -9814,10 +7646,10 @@ __metadata: "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 "@types/testing-library__jest-dom": ~5.14.6 - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 babel-loader: ^8.3.0 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 @@ -9832,7 +7664,7 @@ __metadata: react-dom: ~17.0.2 react-error-boundary: ^3.1.4 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 peerDependencies: "@rocket.chat/core-typings": "*" "@rocket.chat/css-in-js": "*" @@ -9849,18 +7681,18 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/i18n@workspace:packages/i18n" dependencies: - "@babel/core": ~7.22.5 - "@babel/preset-env": ~7.22.5 + "@babel/core": ~7.22.9 + "@babel/preset-env": ~7.22.9 "@babel/preset-typescript": ~7.22.5 "@types/babel__core": ~7.20.1 "@types/babel__preset-env": ~7.9.2 "@types/jest": ~29.5.3 babel-jest: ^29.5.0 - eslint: ^8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 tsup: ^6.7.0 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9877,10 +7709,10 @@ __metadata: dependencies: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/models": "workspace:^" - eslint: ~8.43.0 + eslint: ~8.45.0 mongodb: ^4.12.1 prettier: ~2.8.8 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -9900,8 +7732,8 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/livechat@workspace:packages/livechat" dependencies: - "@babel/eslint-parser": ~7.22.5 - "@babel/preset-env": ~7.22.5 + "@babel/eslint-parser": ~7.22.9 + "@babel/preset-env": ~7.22.9 "@babel/preset-typescript": ~7.22.5 "@rocket.chat/ddp-client": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" @@ -9913,8 +7745,8 @@ __metadata: "@storybook/addon-postcss": ~2.0.0 "@storybook/preact": ~6.5.16 "@storybook/theming": ~6.5.16 - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 autoprefixer: ^9.8.8 babel-loader: ^8.3.0 cross-env: ^7.0.3 @@ -9925,7 +7757,7 @@ __metadata: date-fns: ^2.15.0 desvg-loader: ^0.1.0 emoji-mart: ^3.0.1 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-plugin-import: ~2.26.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 @@ -9964,7 +7796,7 @@ __metadata: stylelint: ^14.9.1 stylelint-order: ^5.0.0 svg-loader: ^0.0.2 - typescript: ~5.1.3 + typescript: ~5.1.6 url-loader: ^4.1.1 webpack: ~4.46.0 webpack-cli: ~4.10.0 @@ -9985,10 +7817,10 @@ __metadata: "@types/jest": ~29.5.3 chalk: ^4.0.0 ejson: ^2.2.3 - eslint: ^8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10032,14 +7864,14 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/meteor@workspace:apps/meteor" dependencies: - "@babel/core": ^7.20.5 - "@babel/eslint-parser": ^7.22.5 - "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 - "@babel/plugin-proposal-optional-chaining": ^7.18.9 - "@babel/preset-env": ^7.20.2 - "@babel/preset-react": ^7.18.6 - "@babel/register": ^7.18.9 - "@babel/runtime": ~7.22.5 + "@babel/core": ~7.22.9 + "@babel/eslint-parser": ~7.22.9 + "@babel/plugin-proposal-nullish-coalescing-operator": ~7.18.6 + "@babel/plugin-proposal-optional-chaining": ~7.21.0 + "@babel/preset-env": ~7.22.9 + "@babel/preset-react": ~7.22.5 + "@babel/register": ~7.22.5 + "@babel/runtime": ~7.22.6 "@bugsnag/js": ~7.20.2 "@bugsnag/plugin-react": ~7.19.0 "@faker-js/faker": ~8.0.2 @@ -10194,8 +8026,8 @@ __metadata: "@types/uuid": ^8.3.4 "@types/xml-crypto": ~1.4.2 "@types/xml-encryption": ~1.2.1 - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 "@xmldom/xmldom": ^0.8.8 adm-zip: 0.5.10 ajv: ^8.11.0 @@ -10245,7 +8077,7 @@ __metadata: emoji-toolkit: ^7.0.1 emojione: ^4.5.0 emojione-assets: ^4.5.0 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-config-prettier: ~8.8.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-import: ~2.26.0 @@ -10382,7 +8214,7 @@ __metadata: turndown: ^7.1.2 twilio: ^3.76.1 twit: ^2.2.11 - typescript: ~5.1.3 + typescript: ~5.1.6 ua-parser-js: ^1.0.35 underscore: ^1.13.6 universal-perf-hooks: ^1.0.1 @@ -10406,11 +8238,11 @@ __metadata: "@rocket.chat/ui-contexts": "workspace:*" "@tanstack/react-query": ^4.16.1 "@types/jest": ~29.5.3 - eslint: ^8.12.0 + eslint: ~8.45.0 jest: ~29.6.1 react: ~17.0.2 ts-jest: ~29.0.5 - typescript: ~5.0.2 + typescript: ~5.1.6 peerDependencies: "@tanstack/react-query": "*" react: "*" @@ -10424,11 +8256,11 @@ __metadata: "@rocket.chat/core-typings": "workspace:^" "@types/jest": ~29.5.3 "@types/node-rsa": ^1.1.1 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10438,10 +8270,10 @@ __metadata: dependencies: "@rocket.chat/model-typings": "workspace:^" "@types/jest": ~29.5.3 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10472,7 +8304,7 @@ __metadata: "@types/node": ^14.18.51 ejson: ^2.2.3 emoji-toolkit: ^7.0.1 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 jest: ~29.6.1 @@ -10482,7 +8314,7 @@ __metadata: mongodb: ^4.12.1 pino: ^8.4.2 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10501,12 +8333,11 @@ __metadata: "@rocket.chat/pdf-worker": "workspace:^" "@rocket.chat/tools": "workspace:^" "@rocket.chat/ui-contexts": "workspace:^" - "@types/eslint": ~8.40.2 "@types/node": ^14.18.51 "@types/polka": ^0.5.4 ejson: ^2.2.3 emoji-toolkit: ^7.0.1 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 mem: ^8.1.1 @@ -10518,7 +8349,7 @@ __metadata: pino: ^8.4.2 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10561,7 +8392,7 @@ __metadata: "@types/testing-library__jest-dom": ~5.14.6 emoji-assets: ^7.0.1 emoji-toolkit: ^7.0.1 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 jest-environment-jsdom: ~29.6.1 moment: ^2.29.4 @@ -10569,7 +8400,7 @@ __metadata: react: ^18.2.0 react-dom: ^18.2.0 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10578,10 +8409,10 @@ __metadata: resolution: "@rocket.chat/poplib@workspace:packages/node-poplib" dependencies: "@types/jest": ~29.5.3 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10597,11 +8428,10 @@ __metadata: "@rocket.chat/models": "workspace:^" "@rocket.chat/presence": "workspace:^" "@rocket.chat/string-helpers": next - "@types/eslint": ~8.40.2 "@types/node": ^14.18.51 "@types/polka": ^0.5.4 ejson: ^2.2.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 mem: ^8.1.1 @@ -10611,7 +8441,7 @@ __metadata: pino: ^8.4.2 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10619,8 +8449,8 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/presence@workspace:ee/packages/presence" dependencies: - "@babel/core": ~7.22.5 - "@babel/preset-env": ~7.22.5 + "@babel/core": ~7.22.9 + "@babel/preset-env": ~7.22.9 "@babel/preset-typescript": ~7.22.5 "@rocket.chat/apps-engine": 1.39.1 "@rocket.chat/core-services": "workspace:^" @@ -10630,10 +8460,10 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@types/node": ^14.18.51 babel-jest: ^29.0.3 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 mongodb: ^4.12.1 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10658,12 +8488,11 @@ __metadata: "@rocket.chat/model-typings": "workspace:^" "@rocket.chat/models": "workspace:^" "@rocket.chat/omnichannel-services": "workspace:^" - "@types/eslint": ~8.40.2 "@types/node": ^14.18.51 "@types/polka": ^0.5.4 ejson: ^2.2.3 emoji-toolkit: ^7.0.1 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 mem: ^8.1.1 @@ -10675,7 +8504,7 @@ __metadata: pino: ^8.4.2 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10683,16 +8512,16 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/random@workspace:packages/random" dependencies: - "@babel/core": ~7.22.5 - "@babel/preset-env": ~7.22.5 + "@babel/core": ~7.22.9 + "@babel/preset-env": ~7.22.9 "@rocket.chat/eslint-config": "workspace:^" - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 - eslint: ~8.43.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 + eslint: ~8.45.0 jest: ~29.6.1 jest-environment-jsdom: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10705,14 +8534,13 @@ __metadata: "@actions/github": ^5.1.1 "@octokit/plugin-throttling": ^6.0.0 "@rocket.chat/eslint-config": "workspace:^" - "@types/eslint": ^8.40.2 "@types/node": ^16.18.36 - eslint: ^8.43.0 + eslint: ~8.45.0 mdast-util-to-string: 2.0.0 remark-parse: 9.0.0 remark-stringify: 9.0.1 semver: ^7.5.2 - typescript: ^5.1.3 + typescript: ~5.1.6 unified: 9.2.2 languageName: unknown linkType: soft @@ -10729,12 +8557,12 @@ __metadata: "@types/jest": ~29.5.3 ajv: ^8.11.0 ajv-formats: ^2.1.1 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 jest-environment-jsdom: ~29.6.1 mongodb: ^4.12.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10757,14 +8585,14 @@ __metadata: dependencies: "@types/jest": ~29.5.3 "@types/proxy-from-env": ^1.0.1 - eslint: ^8.43.0 + eslint: ~8.45.0 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.1 jest: ~29.6.1 node-fetch: 2.3.0 proxy-from-env: ^1.1.0 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10772,15 +8600,15 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/sha256@workspace:packages/sha256" dependencies: - "@babel/core": ~7.22.5 - "@babel/preset-env": ~7.22.5 + "@babel/core": ~7.22.9 + "@babel/preset-env": ~7.22.9 "@rocket.chat/eslint-config": "workspace:^" - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 - eslint: ~8.43.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 + eslint: ~8.45.0 jest: ~29.6.1 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10797,11 +8625,10 @@ __metadata: "@rocket.chat/rest-typings": "workspace:^" "@rocket.chat/string-helpers": next "@types/bcrypt": ^5.0.0 - "@types/eslint": ~8.40.2 "@types/node": ^14.18.51 "@types/polka": ^0.5.4 ejson: ^2.2.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eventemitter3: ^4.0.7 fibers: ^5.0.3 mem: ^8.1.1 @@ -10811,7 +8638,7 @@ __metadata: pino: ^8.4.2 polka: ^0.5.2 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10856,11 +8683,11 @@ __metadata: resolution: "@rocket.chat/tools@workspace:packages/tools" dependencies: "@types/jest": ~29.5.3 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 moment-timezone: ^0.5.43 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 languageName: unknown linkType: soft @@ -10868,7 +8695,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-client@workspace:packages/ui-client" dependencies: - "@babel/core": ~7.22.5 + "@babel/core": ~7.22.9 "@rocket.chat/css-in-js": next "@rocket.chat/fuselage": next "@rocket.chat/fuselage-hooks": next @@ -10892,7 +8719,7 @@ __metadata: "@types/jest": ~29.5.3 "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 @@ -10903,7 +8730,7 @@ __metadata: react-dom: ^17.0.2 react-hook-form: ^7.30.0 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 peerDependencies: "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" @@ -10918,7 +8745,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-composer@workspace:packages/ui-composer" dependencies: - "@babel/core": ~7.22.5 + "@babel/core": ~7.22.9 "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/fuselage": next "@rocket.chat/icons": next @@ -10931,14 +8758,14 @@ __metadata: "@storybook/testing-library": ~0.0.13 "@types/babel__core": ~7.20.1 "@types/jest": ~29.5.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 jest: ~29.6.1 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 peerDependencies: "@rocket.chat/fuselage": "*" "@rocket.chat/icons": "*" @@ -10959,13 +8786,13 @@ __metadata: "@types/react": ~17.0.62 "@types/react-dom": ~17.0.20 "@types/use-sync-external-store": ^0.0.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-plugin-react-hooks: ^4.6.0 jest: ~29.6.1 mongodb: ^4.12.1 react: ~17.0.2 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 use-sync-external-store: ^1.2.0 peerDependencies: "@rocket.chat/core-typings": "workspace:^" @@ -11008,7 +8835,7 @@ __metadata: "@storybook/testing-library": ~0.0.13 "@types/jest": ~29.5.3 "@types/react": ~17.0.62 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-plugin-anti-trojan-source: ~1.1.1 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 @@ -11017,7 +8844,7 @@ __metadata: react: ~17.0.2 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 peerDependencies: "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" @@ -11031,7 +8858,7 @@ __metadata: version: 0.0.0-use.local resolution: "@rocket.chat/ui-video-conf@workspace:packages/ui-video-conf" dependencies: - "@babel/core": ~7.22.5 + "@babel/core": ~7.22.9 "@rocket.chat/css-in-js": next "@rocket.chat/emitter": next "@rocket.chat/eslint-config": "workspace:^" @@ -11049,14 +8876,14 @@ __metadata: "@storybook/testing-library": ~0.0.13 "@types/babel__core": ~7.20.1 "@types/jest": ~29.5.3 - eslint: ~8.43.0 + eslint: ~8.45.0 eslint-plugin-react: ~7.32.2 eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-storybook: ~0.6.12 jest: ~29.6.1 react-docgen-typescript-plugin: ~1.0.5 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 peerDependencies: "@rocket.chat/css-in-js": "*" "@rocket.chat/fuselage": "*" @@ -11091,11 +8918,11 @@ __metadata: "@types/react-beautiful-dnd": ^13.1.4 "@types/react-dom": ~17.0.20 "@types/use-subscription": ^1.0.0 - "@typescript-eslint/eslint-plugin": ~5.60.0 - "@typescript-eslint/parser": ~5.60.0 + "@typescript-eslint/eslint-plugin": ~5.60.1 + "@typescript-eslint/parser": ~5.60.1 "@vitejs/plugin-react": ^4.0.0 codemirror: ^6.0.1 - eslint: ^8.43.0 + eslint: ~8.45.0 eslint-plugin-react-hooks: ^4.6.0 eslint-plugin-react-refresh: ^0.4.1 eslint4b-prebuilt: ^6.7.2 @@ -11106,7 +8933,7 @@ __metadata: react-router-dom: ^6.11.2 react-split-pane: ^0.1.92 react-virtuoso: ^4.3.10 - typescript: ^5.1.3 + typescript: ~5.1.6 use-subscription: ^1.8.0 vite: ^4.3.9 languageName: unknown @@ -11122,11 +8949,11 @@ __metadata: "@tanstack/react-query": ^4.16.1 "@testing-library/react": ^13.3.0 "@types/jest": ~29.5.3 - eslint: ~8.43.0 + eslint: ~8.45.0 jest: ~29.6.1 react-hook-form: ^7.34.2 ts-jest: ~29.0.5 - typescript: ~5.1.3 + typescript: ~5.1.6 peerDependencies: "@rocket.chat/layout": "*" "@rocket.chat/ui-contexts": "*" @@ -11169,13 +8996,6 @@ __metadata: languageName: node linkType: hard -"@sinclair/typebox@npm:^0.25.16": - version: 0.25.24 - resolution: "@sinclair/typebox@npm:0.25.24" - checksum: 10219c58f40b8414c50b483b0550445e9710d4fe7b2c4dccb9b66533dd90ba8e024acc776026cebe81e87f06fa24b07fdd7bc30dd277eb9cc386ec50151a3026 - languageName: node - linkType: hard - "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -13011,33 +10831,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7": - version: 7.20.0 - resolution: "@types/babel__core@npm:7.20.0" - dependencies: - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 - "@types/babel__generator": "*" - "@types/babel__template": "*" - "@types/babel__traverse": "*" - checksum: 49b601a0a7637f1f387442c8156bd086cfd10ff4b82b0e1994e73a6396643b5435366fb33d6b604eade8467cca594ef97adcbc412aede90bb112ebe88d0ad6df - languageName: node - linkType: hard - -"@types/babel__core@npm:^7.1.14": - version: 7.1.20 - resolution: "@types/babel__core@npm:7.1.20" - dependencies: - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 - "@types/babel__generator": "*" - "@types/babel__template": "*" - "@types/babel__traverse": "*" - checksum: a09c4f0456552547a5b8a5a009a3daec4d362f622168f8e08bda0ded2da0a65ab0b1642e23c433b3616721f5701dc34a996c5bde5baeaea53eda98f438043f2c - languageName: node - linkType: hard - -"@types/babel__core@npm:~7.20.1": +"@types/babel__core@npm:^7, @types/babel__core@npm:^7.1.14, @types/babel__core@npm:~7.20.1": version: 7.20.1 resolution: "@types/babel__core@npm:7.20.1" dependencies: @@ -13174,14 +10968,7 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:*": - version: 4.3.1 - resolution: "@types/chai@npm:4.3.1" - checksum: 2ee246b76c469cd620a7a1876a73bc597074361b67d547b4bd96a0c1adb43597ede2d8589ab626192e14349d83cbb646cc11e2c179eeeb43ff11596de94d82c4 - languageName: node - linkType: hard - -"@types/chai@npm:^4.3.5": +"@types/chai@npm:*, @types/chai@npm:^4.3.5": version: 4.3.5 resolution: "@types/chai@npm:4.3.5" checksum: c8f26a88c6b5b53a3275c7f5ff8f107028e3cbb9ff26795fff5f3d9dea07106a54ce9e2dce5e40347f7c4cc35657900aaf0c83934a25a1ae12e61e0f5516e431 @@ -13324,40 +11111,30 @@ __metadata: languageName: node linkType: hard -"@types/eslint@npm:*": - version: 8.37.0 - resolution: "@types/eslint@npm:8.37.0" +"@types/eslint@npm:*, @types/eslint@npm:~8.44.0": + version: 8.44.0 + resolution: "@types/eslint@npm:8.44.0" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: 06d3b3fba12004294591b5c7a52e3cec439472195da54e096076b1f2ddfbb8a445973b9681046dd530a6ac31eca502f635abc1e3ce37d03513089358e6f822ee + checksum: 2655f409a4ecdd64bb9dd9eb6715e7a2ac30c0e7f902b414e10dbe9d6d497baa5a0f13105e1f7bd5ad7a913338e2ab4bed1faf192a7a0d27d1acd45ba79d3f69 languageName: node linkType: hard -"@types/eslint@npm:^8.40.2, @types/eslint@npm:~8.40.2": - version: 8.40.2 - resolution: "@types/eslint@npm:8.40.2" - dependencies: - "@types/estree": "*" - "@types/json-schema": "*" - checksum: a4780e45e677e3af21c44a900846996cb6d9ae8f71d51940942a047163ae93a05444392c005f491ed46aa169f3b25f8be125ab42c5d8bdb571154bf62a7c828a +"@types/estree@npm:*, @types/estree@npm:^1.0.0": + version: 1.0.1 + resolution: "@types/estree@npm:1.0.1" + checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^0.0.51": +"@types/estree@npm:^0.0.51": version: 0.0.51 resolution: "@types/estree@npm:0.0.51" checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 languageName: node linkType: hard -"@types/estree@npm:^1.0.0": - version: 1.0.1 - resolution: "@types/estree@npm:1.0.1" - checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d - languageName: node - linkType: hard - "@types/express-rate-limit@npm:^5.1.3": version: 5.1.3 resolution: "@types/express-rate-limit@npm:5.1.3" @@ -13367,18 +11144,7 @@ __metadata: languageName: node linkType: hard -"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.18": - version: 4.17.31 - resolution: "@types/express-serve-static-core@npm:4.17.31" - dependencies: - "@types/node": "*" - "@types/qs": "*" - "@types/range-parser": "*" - checksum: 009bfbe1070837454a1056aa710d0390ee5fb8c05dfe5a1691cc3e2ca88dc256f80e1ca27cb51a978681631d2f6431bfc9ec352ea46dd0c6eb183d0170bde5df - languageName: node - linkType: hard - -"@types/express-serve-static-core@npm:^4.17.33": +"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": version: 4.17.35 resolution: "@types/express-serve-static-core@npm:4.17.35" dependencies: @@ -13390,19 +11156,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.8": - version: 4.17.13 - resolution: "@types/express@npm:4.17.13" - dependencies: - "@types/body-parser": "*" - "@types/express-serve-static-core": ^4.17.18 - "@types/qs": "*" - "@types/serve-static": "*" - checksum: 12a2a0e6c4b993fc0854bec665906788aea0d8ee4392389d7a98a5de1eefdd33c9e1e40a91f3afd274011119c506f7b4126acb97fae62ae20b654974d44cba12 - languageName: node - linkType: hard - -"@types/express@npm:^4.17.17": +"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.17, @types/express@npm:^4.17.8": version: 4.17.17 resolution: "@types/express@npm:4.17.17" dependencies: @@ -13571,17 +11325,7 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*": - version: 29.5.0 - resolution: "@types/jest@npm:29.5.0" - dependencies: - expect: ^29.0.0 - pretty-format: ^29.0.0 - checksum: cd877e5c56d299cceb8bfdcbb1a77723c706750dd3c3bc47403bc3599b8faff590a3b009c68bb5b11bf7a8c77d1fb01de5e124329b4a08e65f1cdda28b0ecdb8 - languageName: node - linkType: hard - -"@types/jest@npm:~29.5.3": +"@types/jest@npm:*, @types/jest@npm:~29.5.3": version: 29.5.3 resolution: "@types/jest@npm:29.5.3" dependencies: @@ -13723,14 +11467,7 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:*, @types/lodash@npm:^4.14.167": - version: 4.14.182 - resolution: "@types/lodash@npm:4.14.182" - checksum: 7dd137aa9dbabd632408bd37009d984655164fa1ecc3f2b6eb94afe35bf0a5852cbab6183148d883e9c73a958b7fec9a9bcf7c8e45d41195add6a18c34958209 - languageName: node - linkType: hard - -"@types/lodash@npm:^4.14.195": +"@types/lodash@npm:*, @types/lodash@npm:^4.14.167, @types/lodash@npm:^4.14.195": version: 4.14.195 resolution: "@types/lodash@npm:4.14.195" checksum: 39b75ca635b3fa943d17d3d3aabc750babe4c8212485a4df166fe0516e39288e14b0c60afc6e21913cc0e5a84734633c71e617e2bd14eaa1cf51b8d7799c432e @@ -13889,44 +11626,21 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^14.0.10 || ^16.0.0, @types/node@npm:^14.14.20 || ^16.0.0": - version: 16.11.39 - resolution: "@types/node@npm:16.11.39" - checksum: bc97b9773ac6b3194800f990b349fad7f66c6126dacef59291b10a2c8b6813d6f67f947b7e12a6c9952790f7065d576fe38355b8fe034a6af60f317cfc570f69 - languageName: node - linkType: hard - -"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37": - version: 14.18.21 - resolution: "@types/node@npm:14.18.21" - checksum: 4ed35b76609647a4e36a194702e31cdda9ed42174ddaf7937bc5498984e98a99e8a42ea895ea17dd9c5ec18080112c29ab670c34f90eb9f7a4703b85b31e34fa - languageName: node - linkType: hard - -"@types/node@npm:^14.18.51": - version: 14.18.51 - resolution: "@types/node@npm:14.18.51" - checksum: 0960a31d2ac605763fe79c8edcee3cb48257d345ce417c019d84ff5d8cd92dd0937674814ab3f169346b4259c29f640556006bcb2c54cfb3e63fa0cf728d320e - languageName: node - linkType: hard - -"@types/node@npm:^16.18.36": +"@types/node@npm:^14.0.10 || ^16.0.0, @types/node@npm:^14.14.20 || ^16.0.0, @types/node@npm:^16.18.36": version: 16.18.36 resolution: "@types/node@npm:16.18.36" checksum: a9d138fa1269079c60daad6984713dc0b713983f8b34a83edbc6d7957b2e38beab9b2598c9fe99f19d073e20bc212a18aaf82eabdc23ef64dce7d2089a9aab2a languageName: node linkType: hard -"@types/nodemailer@npm:*": - version: 6.4.7 - resolution: "@types/nodemailer@npm:6.4.7" - dependencies: - "@types/node": "*" - checksum: dc2a33a89135e04a5bea4921e8645e8453b90e3c3b05f0646f05071c5951ab697ea49ea1e503a690f04cb0a6abfc54967325c5a4036356793cfbb64ba64fb141 +"@types/node@npm:^14.0.26, @types/node@npm:^14.14.37, @types/node@npm:^14.18.51": + version: 14.18.51 + resolution: "@types/node@npm:14.18.51" + checksum: 0960a31d2ac605763fe79c8edcee3cb48257d345ce417c019d84ff5d8cd92dd0937674814ab3f169346b4259c29f640556006bcb2c54cfb3e63fa0cf728d320e languageName: node linkType: hard -"@types/nodemailer@npm:^6.4.8": +"@types/nodemailer@npm:*, @types/nodemailer@npm:^6.4.8": version: 6.4.8 resolution: "@types/nodemailer@npm:6.4.8" dependencies: @@ -14102,12 +11816,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:<18.0.0": - version: 17.0.17 - resolution: "@types/react-dom@npm:17.0.17" +"@types/react-dom@npm:<18.0.0, @types/react-dom@npm:~17.0.20": + version: 17.0.20 + resolution: "@types/react-dom@npm:17.0.20" dependencies: "@types/react": ^17 - checksum: 23caf98aa03e968811560f92a2c8f451694253ebe16b670929b24eaf0e7fa62ba549abe9db0ac028a9d8a9086acd6ab9c6c773f163fa21224845edbc00ba6232 + checksum: 525439fb14a033fc5dbe74711ecc50ec82273a528df9656594066a6219401e975101dafffd15d9a1a57a9442d52ea0c92eaacae09554dde27cd792e773f67467 languageName: node linkType: hard @@ -14120,15 +11834,6 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:~17.0.20": - version: 17.0.20 - resolution: "@types/react-dom@npm:17.0.20" - dependencies: - "@types/react": ^17 - checksum: 525439fb14a033fc5dbe74711ecc50ec82273a528df9656594066a6219401e975101dafffd15d9a1a57a9442d52ea0c92eaacae09554dde27cd792e773f67467 - languageName: node - linkType: hard - "@types/react-redux@npm:^7.1.20": version: 7.1.25 resolution: "@types/react-redux@npm:7.1.25" @@ -14141,18 +11846,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^17": - version: 17.0.58 - resolution: "@types/react@npm:17.0.58" - dependencies: - "@types/prop-types": "*" - "@types/scheduler": "*" - csstype: ^3.0.2 - checksum: 4eaf32b86c43f388c681e34a00921c508dd88a1d1022aebfadc5fe802b7c5bed863de1a17eed31e43ca2d65222952dfe79a022055a0e6e4e1ad89fc5a42ec05e - languageName: node - linkType: hard - -"@types/react@npm:~17.0.62": +"@types/react@npm:*, @types/react@npm:^17, @types/react@npm:~17.0.62": version: 17.0.62 resolution: "@types/react@npm:17.0.62" dependencies: @@ -14378,16 +12072,7 @@ __metadata: languageName: node linkType: hard -"@types/testing-library__jest-dom@npm:^5.9.1": - version: 5.14.5 - resolution: "@types/testing-library__jest-dom@npm:5.14.5" - dependencies: - "@types/jest": "*" - checksum: dcb05416758fe88c1f4f3aa97b4699fcb46a5ed8f53c6b81721e66155452a48caf12ecb97dfdfd4130678e65efd66b9fca0ac434b3d63affec84842a84a6bf38 - languageName: node - linkType: hard - -"@types/testing-library__jest-dom@npm:~5.14.6": +"@types/testing-library__jest-dom@npm:^5.9.1, @types/testing-library__jest-dom@npm:~5.14.6": version: 5.14.6 resolution: "@types/testing-library__jest-dom@npm:5.14.6" dependencies: @@ -14540,16 +12225,7 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.1": - version: 8.5.4 - resolution: "@types/ws@npm:8.5.4" - dependencies: - "@types/node": "*" - checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1 - languageName: node - linkType: hard - -"@types/ws@npm:^8.5.5": +"@types/ws@npm:^8.5.1, @types/ws@npm:^8.5.5": version: 8.5.5 resolution: "@types/ws@npm:8.5.5" dependencies: @@ -14611,14 +12287,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:~5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.60.0" +"@typescript-eslint/eslint-plugin@npm:~5.60.1": + version: 5.60.1 + resolution: "@typescript-eslint/eslint-plugin@npm:5.60.1" dependencies: "@eslint-community/regexpp": ^4.4.0 - "@typescript-eslint/scope-manager": 5.60.0 - "@typescript-eslint/type-utils": 5.60.0 - "@typescript-eslint/utils": 5.60.0 + "@typescript-eslint/scope-manager": 5.60.1 + "@typescript-eslint/type-utils": 5.60.1 + "@typescript-eslint/utils": 5.60.1 debug: ^4.3.4 grapheme-splitter: ^1.0.4 ignore: ^5.2.0 @@ -14631,53 +12307,43 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 61dd70a1ea9787e69d0d4cd14f6a4c94ba786b535a3f519ade7926d965ee1d4f8fefa8bf0224ee57c5c6517eec3674c0fd06f9226536aa428c2bdddeed1e70f4 + checksum: 6ea3fdc64b216ee709318bfce1573cd8d90836150f0075aaa8755c347541af9ec026043e538a3264d28d1b32ff49b1fd7c6163826b8513f19f0957fefccf7752 languageName: node linkType: hard -"@typescript-eslint/parser@npm:~5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/parser@npm:5.60.0" +"@typescript-eslint/parser@npm:~5.60.1": + version: 5.60.1 + resolution: "@typescript-eslint/parser@npm:5.60.1" dependencies: - "@typescript-eslint/scope-manager": 5.60.0 - "@typescript-eslint/types": 5.60.0 - "@typescript-eslint/typescript-estree": 5.60.0 + "@typescript-eslint/scope-manager": 5.60.1 + "@typescript-eslint/types": 5.60.1 + "@typescript-eslint/typescript-estree": 5.60.1 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 94e7931a5b356b16638b281b8e1d661f8b1660f0c75a323537f68b311dae91b7a575a0a019d4ea05a79cc5d42b5cb41cc367205691cdfd292ef96a3b66b1e58b - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/scope-manager@npm:5.58.0" - dependencies: - "@typescript-eslint/types": 5.58.0 - "@typescript-eslint/visitor-keys": 5.58.0 - checksum: f0d3df5cc3c461fe63ef89ad886b53c239cc7c1d9061d83d8a9d9c8e087e5501eac84bebff8a954728c17ccea191f235686373d54d2b8b6370af2bcf2b18e062 + checksum: 08f1552ab0da178524a8de3654d2fb7c8ecb9efdad8e771c9cbf4af555c42e77d17b2c182d139a531cc76c3cabd091d1d25024c2c215cb809dca8b147c8a493c languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/scope-manager@npm:5.60.0" +"@typescript-eslint/scope-manager@npm:5.60.1": + version: 5.60.1 + resolution: "@typescript-eslint/scope-manager@npm:5.60.1" dependencies: - "@typescript-eslint/types": 5.60.0 - "@typescript-eslint/visitor-keys": 5.60.0 - checksum: b21ee1ef57be948a806aa31fd65a9186766b3e1a727030dc47025edcadc54bd1aa6133a439acd5f44a93e2b983dd55bc5571bb01cb834461dab733682d66256a + "@typescript-eslint/types": 5.60.1 + "@typescript-eslint/visitor-keys": 5.60.1 + checksum: 32c0786123f12fbb861aba3527471134a2e9978c7f712e0d7650080651870903482aed72a55f81deba9493118c1ca3c57edaaaa75d7acd9892818e3e9cc341ef languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/type-utils@npm:5.60.0" +"@typescript-eslint/type-utils@npm:5.60.1": + version: 5.60.1 + resolution: "@typescript-eslint/type-utils@npm:5.60.1" dependencies: - "@typescript-eslint/typescript-estree": 5.60.0 - "@typescript-eslint/utils": 5.60.0 + "@typescript-eslint/typescript-estree": 5.60.1 + "@typescript-eslint/utils": 5.60.1 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -14685,72 +12351,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: b90ce97592f2db899d88d7a325fec4d2ea11a7b8b4306787310890c27fb51862a6c003675252e9dc465908f791ad5320ea7307260ecd10e89ca1d209fbf8616d - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/types@npm:5.52.0" - checksum: 018940d61aebf7cf3f7de1b9957446e2ea01f08fe950bef4788c716a3a88f7c42765fe7d80152b0d0428fcd4bd3ace2dfa8c459ba1c59d9a84e951642180f869 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/types@npm:5.58.0" - checksum: 8622a73d73220c4a7111537825f488c0271272032a1d4e129dc722bc6e8b3ec84f64469b2ca3b8dae7da3a9c18953ce1449af51f5f757dad60835eb579ad1d2c - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/types@npm:5.60.0" - checksum: 48f29e5c084c5663cfed1a6c4458799a6690a213e7861a24501f9b96698ae59e89a1df1c77e481777e4da78f1b0a5573a549f7b8880e3f4071a7a8b686254db8 - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.52.0" - dependencies: - "@typescript-eslint/types": 5.52.0 - "@typescript-eslint/visitor-keys": 5.52.0 - debug: ^4.3.4 - globby: ^11.1.0 - is-glob: ^4.0.3 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 67d396907fee3d6894e26411a5098a37f07e5d50343189e6361ff7db91c74a7ffe2abd630d11f14c2bda1f4af13edf52b80b11cbccb55b44079c7cec14c9e108 + checksum: f8d5f87b5441d5c671f69631efd103f5f45e0cb7dbe0131a5b4234a5208ac845041219e8baaa3adc341e82a602165dd6fabf4fd06964d0109d0875425c8ac918 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.58.0" - dependencies: - "@typescript-eslint/typescript-estree": 5.52.0 - "@typescript-eslint/utils": 5.52.0 - debug: ^4.3.4 - tsutils: ^3.21.0 - peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: 51b668ec858db0c040a71dff526273945cee4ba5a9b240528d503d02526685882d900cf071c6636a4d9061ed3fd4a7274f7f1a23fba55c4b48b143344b4009c7 +"@typescript-eslint/types@npm:5.60.1": + version: 5.60.1 + resolution: "@typescript-eslint/types@npm:5.60.1" + checksum: 766b6c857493b72a8f515e6a8e409476a317b7a7f0401fbcdf18f417839fca004dcaf06f58eb5ba00777e3ca9c68cd2f56fda79f3a8eb8a418095b5b1f625712 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.60.0" +"@typescript-eslint/typescript-estree@npm:5.60.1": + version: 5.60.1 + resolution: "@typescript-eslint/typescript-estree@npm:5.60.1" dependencies: - "@typescript-eslint/types": 5.60.0 - "@typescript-eslint/visitor-keys": 5.60.0 + "@typescript-eslint/types": 5.60.1 + "@typescript-eslint/visitor-keys": 5.60.1 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -14759,91 +12376,35 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 0f4f342730ead42ba60b5fca4bf1950abebd83030010c38b5df98ff9fd95d0ce1cfc3974a44c90c65f381f4f172adcf1a540e018d7968cc845d937bf6c734dae + checksum: 5bb9d08c3cbc303fc64647878cae37283c4cfa9e3ed00da02ee25dc2e46798a1ad6964c9f04086f0134716671357e6569a65ea0ae75f0f3ff94ae67666385c6f languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/utils@npm:5.52.0" +"@typescript-eslint/utils@npm:5.60.1, @typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.45.0, @typescript-eslint/utils@npm:^5.58.0": + version: 5.60.1 + resolution: "@typescript-eslint/utils@npm:5.60.1" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@types/json-schema": ^7.0.9 "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.58.0 - "@typescript-eslint/types": 5.58.0 - "@typescript-eslint/typescript-estree": 5.58.0 + "@typescript-eslint/scope-manager": 5.60.1 + "@typescript-eslint/types": 5.60.1 + "@typescript-eslint/typescript-estree": 5.60.1 eslint-scope: ^5.1.1 semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 01906be5262ece36537e9d586e4d2d4791e05752a9354bcb42b1f5bf965f53daa13309c61c3dff5e201ea28c298e4e01cf0c93738afa0099fea0da3b1d8cb3a5 + checksum: 00c1adaa09d5d5be947e98962a78c21ed08c3ac46dd5ddd7b78f6102537d50afd4578a42a3e09a24dd51f5bc493f0b968627b4423647540164b2d2380afa9246 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/utils@npm:5.60.0" +"@typescript-eslint/visitor-keys@npm:5.60.1": + version: 5.60.1 + resolution: "@typescript-eslint/visitor-keys@npm:5.60.1" dependencies: - "@eslint-community/eslint-utils": ^4.2.0 - "@types/json-schema": ^7.0.9 - "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.60.0 - "@typescript-eslint/types": 5.60.0 - "@typescript-eslint/typescript-estree": 5.60.0 - eslint-scope: ^5.1.1 - semver: ^7.3.7 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: cbe56567f0b53e24ad7ef7d2fb4cdc8596e2559c21ee639aa0560879b6216208550e51e9d8ae4b388ff21286809c6dc985cec66738294871051396a8ae5bccbc - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:^5.10.0, @typescript-eslint/utils@npm:^5.45.0, @typescript-eslint/utils@npm:^5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/utils@npm:5.58.0" - dependencies: - "@eslint-community/eslint-utils": ^4.2.0 - "@types/json-schema": ^7.0.9 - "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.58.0 - "@typescript-eslint/types": 5.58.0 - "@typescript-eslint/typescript-estree": 5.58.0 - eslint-scope: ^5.1.1 - semver: ^7.3.7 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: c618ae67963ecf96b1492c09afaeb363f542f0d6780bcac4af3c26034e3b20034666b2d523aa94821df813aafb57a0b150a7d5c2224fe8257452ad1de2237a58 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.52.0": - version: 5.52.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.52.0" - dependencies: - "@typescript-eslint/types": 5.52.0 - eslint-visitor-keys: ^3.3.0 - checksum: 33b44f0cd35b7b47f34e89d52e47b8d8200f55af306b22db4de104d79f65907458ea022e548f50d966e32fea150432ac9c1ae65b3001b0ad2ac8a17c0211f370 - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.58.0": - version: 5.58.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.58.0" - dependencies: - "@typescript-eslint/types": 5.58.0 - eslint-visitor-keys: ^3.3.0 - checksum: ab2d1f37660559954c840429ef78bbf71834063557e3e68e435005b4987970b9356fdf217ead53f7a57f66f5488dc478062c5c44bf17053a8bf041733539b98f - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:5.60.0": - version: 5.60.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.60.0" - dependencies: - "@typescript-eslint/types": 5.60.0 + "@typescript-eslint/types": 5.60.1 eslint-visitor-keys: ^3.3.0 - checksum: d39b2485d030f9755820d0f6f3748a8ec44e1ca23cb36ddcba67a9eb1f258c8ec83c61fc015c50e8f4a00d05df62d719dbda445625e3e71a64a659f1d248157e + checksum: 137f6a6f8efb398969087147b59f99f7d0deed044d89d7efce3631bb90bc32e3a13a5cee6a65e1c9830862c5c4402ac1a9b2c9e31fe46d1716602af2813bffae languageName: node linkType: hard @@ -14886,16 +12447,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/ast@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ast@npm:1.11.1" - dependencies: - "@webassemblyjs/helper-numbers": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - checksum: 1eee1534adebeece635362f8e834ae03e389281972611408d64be7895fc49f48f98fddbbb5339bf8a72cb101bcb066e8bca3ca1bf1ef47dadf89def0395a8d87 - languageName: node - linkType: hard - "@webassemblyjs/ast@npm:1.11.6, @webassemblyjs/ast@npm:^1.11.5": version: 1.11.6 resolution: "@webassemblyjs/ast@npm:1.11.6" @@ -14917,13 +12468,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/floating-point-hex-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.1" - checksum: b8efc6fa08e4787b7f8e682182d84dfdf8da9d9c77cae5d293818bc4a55c1f419a87fa265ab85252b3e6c1fd323d799efea68d825d341a7c365c64bc14750e97 - languageName: node - linkType: hard - "@webassemblyjs/floating-point-hex-parser@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.6" @@ -14938,13 +12482,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-api-error@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-api-error@npm:1.11.1" - checksum: 0792813f0ed4a0e5ee0750e8b5d0c631f08e927f4bdfdd9fe9105dc410c786850b8c61bff7f9f515fdfb149903bec3c976a1310573a4c6866a94d49bc7271959 - languageName: node - linkType: hard - "@webassemblyjs/helper-api-error@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-api-error@npm:1.11.6" @@ -14959,13 +12496,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-buffer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-buffer@npm:1.11.1" - checksum: a337ee44b45590c3a30db5a8b7b68a717526cf967ada9f10253995294dbd70a58b2da2165222e0b9830cd4fc6e4c833bf441a721128d1fe2e9a7ab26b36003ce - languageName: node - linkType: hard - "@webassemblyjs/helper-buffer@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-buffer@npm:1.11.6" @@ -15005,17 +12535,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-numbers@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-numbers@npm:1.11.1" - dependencies: - "@webassemblyjs/floating-point-hex-parser": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 - "@xtuc/long": 4.2.2 - checksum: 44d2905dac2f14d1e9b5765cf1063a0fa3d57295c6d8930f6c59a36462afecc6e763e8a110b97b342a0f13376166c5d41aa928e6ced92e2f06b071fd0db59d3a - languageName: node - linkType: hard - "@webassemblyjs/helper-numbers@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-numbers@npm:1.11.6" @@ -15027,13 +12546,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-wasm-bytecode@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.1" - checksum: eac400113127832c88f5826bcc3ad1c0db9b3dbd4c51a723cfdb16af6bfcbceb608170fdaac0ab7731a7e18b291be7af68a47fcdb41cfe0260c10857e7413d97 - languageName: node - linkType: hard - "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6" @@ -15048,18 +12560,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/helper-wasm-section@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - checksum: 617696cfe8ecaf0532763162aaf748eb69096fb27950219bb87686c6b2e66e11cd0614d95d319d0ab1904bc14ebe4e29068b12c3e7c5e020281379741fe4bedf - languageName: node - linkType: hard - "@webassemblyjs/helper-wasm-section@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.6" @@ -15084,15 +12584,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/ieee754@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ieee754@npm:1.11.1" - dependencies: - "@xtuc/ieee754": ^1.2.0 - checksum: 23a0ac02a50f244471631802798a816524df17e56b1ef929f0c73e3cde70eaf105a24130105c60aff9d64a24ce3b640dad443d6f86e5967f922943a7115022ec - languageName: node - linkType: hard - "@webassemblyjs/ieee754@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/ieee754@npm:1.11.6" @@ -15111,15 +12602,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/leb128@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/leb128@npm:1.11.1" - dependencies: - "@xtuc/long": 4.2.2 - checksum: 33ccc4ade2f24de07bf31690844d0b1ad224304ee2062b0e464a610b0209c79e0b3009ac190efe0e6bd568b0d1578d7c3047fc1f9d0197c92fc061f56224ff4a - languageName: node - linkType: hard - "@webassemblyjs/leb128@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/leb128@npm:1.11.6" @@ -15138,13 +12620,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/utf8@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/utf8@npm:1.11.1" - checksum: 972c5cfc769d7af79313a6bfb96517253a270a4bf0c33ba486aa43cac43917184fb35e51dfc9e6b5601548cd5931479a42e42c89a13bb591ffabebf30c8a6a0b - languageName: node - linkType: hard - "@webassemblyjs/utf8@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/utf8@npm:1.11.6" @@ -15159,22 +12634,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-edit@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-edit@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/helper-wasm-section": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-opt": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - "@webassemblyjs/wast-printer": 1.11.1 - checksum: 6d7d9efaec1227e7ef7585a5d7ff0be5f329f7c1c6b6c0e906b18ed2e9a28792a5635e450aca2d136770d0207225f204eff70a4b8fd879d3ac79e1dcc26dbeb9 - languageName: node - linkType: hard - "@webassemblyjs/wasm-edit@npm:1.9.0": version: 1.9.0 resolution: "@webassemblyjs/wasm-edit@npm:1.9.0" @@ -15207,19 +12666,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-gen@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-gen@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1f6921e640293bf99fb16b21e09acb59b340a79f986c8f979853a0ae9f0b58557534b81e02ea2b4ef11e929d946708533fd0693c7f3712924128fdafd6465f5b - languageName: node - linkType: hard - "@webassemblyjs/wasm-gen@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wasm-gen@npm:1.11.6" @@ -15246,18 +12692,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-opt@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-opt@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - checksum: 21586883a20009e2b20feb67bdc451bbc6942252e038aae4c3a08e6f67b6bae0f5f88f20bfc7bd0452db5000bacaf5ab42b98cf9aa034a6c70e9fc616142e1db - languageName: node - linkType: hard - "@webassemblyjs/wasm-opt@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wasm-opt@npm:1.11.6" @@ -15282,20 +12716,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wasm-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-parser@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1521644065c360e7b27fad9f4bb2df1802d134dd62937fa1f601a1975cde56bc31a57b6e26408b9ee0228626ff3ba1131ae6f74ffb7d718415b6528c5a6dbfc2 - languageName: node - linkType: hard - "@webassemblyjs/wasm-parser@npm:1.11.6, @webassemblyjs/wasm-parser@npm:^1.11.5": version: 1.11.6 resolution: "@webassemblyjs/wasm-parser@npm:1.11.6" @@ -15338,16 +12758,6 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/wast-printer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wast-printer@npm:1.11.1" - dependencies: - "@webassemblyjs/ast": 1.11.1 - "@xtuc/long": 4.2.2 - checksum: f15ae4c2441b979a3b4fce78f3d83472fb22350c6dc3fd34bfe7c3da108e0b2360718734d961bba20e7716cb8578e964b870da55b035e209e50ec9db0378a3f7 - languageName: node - linkType: hard - "@webassemblyjs/wast-printer@npm:1.11.6": version: 1.11.6 resolution: "@webassemblyjs/wast-printer@npm:1.11.6" @@ -15402,14 +12812,14 @@ __metadata: languageName: node linkType: hard -"@xmldom/xmldom@npm:0.8.7, @xmldom/xmldom@npm:^0.8.5": +"@xmldom/xmldom@npm:0.8.7": version: 0.8.7 resolution: "@xmldom/xmldom@npm:0.8.7" checksum: 593d4429c2281ee7799adcb6ff8604b68cf30ce0721537e3e380287b423e67c7ac197d90987f932b4fd3febc409ded8435706e7f90fbba6e22e08740477341d1 languageName: node linkType: hard -"@xmldom/xmldom@npm:^0.8.8": +"@xmldom/xmldom@npm:^0.8.5, @xmldom/xmldom@npm:^0.8.8": version: 0.8.8 resolution: "@xmldom/xmldom@npm:0.8.8" checksum: 5f5fc0482fcc599f62e3009516932a265e00f1bb2093fe2c76f3f8d9bfebdd13246f48d4132c9b301c7a573f0fa8712e56aa747dce75b179c2b73f1dde7b5f42 @@ -15504,15 +12914,6 @@ __metadata: languageName: node linkType: hard -"acorn-import-assertions@npm:^1.7.6": - version: 1.8.0 - resolution: "acorn-import-assertions@npm:1.8.0" - peerDependencies: - acorn: ^8 - checksum: 5c4cf7c850102ba7ae0eeae0deb40fb3158c8ca5ff15c0bca43b5c47e307a1de3d8ef761788f881343680ea374631ae9e9615ba8876fee5268dbe068c98bcba6 - languageName: node - linkType: hard - "acorn-import-assertions@npm:^1.9.0": version: 1.9.0 resolution: "acorn-import-assertions@npm:1.9.0" @@ -15563,25 +12964,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.0, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.1": - version: 8.8.2 - resolution: "acorn@npm:8.8.2" - bin: - acorn: bin/acorn - checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 - languageName: node - linkType: hard - -"acorn@npm:^8.8.2": - version: 8.9.0 - resolution: "acorn@npm:8.9.0" - bin: - acorn: bin/acorn - checksum: 25dfb94952386ecfb847e61934de04a4e7c2dc21c2e700fc4e2ef27ce78cb717700c4c4f279cd630bb4774948633c3859fc16063ec8573bda4568e0a312e6744 - languageName: node - linkType: hard - -"acorn@npm:^8.9.0": +"acorn@npm:^8.1.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.0, acorn@npm:^8.7.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.10.0 resolution: "acorn@npm:8.10.0" bin: @@ -15733,7 +13116,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:^8.8.0": +"ajv@npm:^8.0.0, ajv@npm:^8.0.1, ajv@npm:^8.11.0, ajv@npm:^8.8.0": version: 8.12.0 resolution: "ajv@npm:8.12.0" dependencies: @@ -15745,18 +13128,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.1, ajv@npm:^8.11.0": - version: 8.11.0 - resolution: "ajv@npm:8.11.0" - dependencies: - fast-deep-equal: ^3.1.1 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef - languageName: node - linkType: hard - "alphanum-sort@npm:^1.0.0": version: 1.0.2 resolution: "alphanum-sort@npm:1.0.2" @@ -15812,7 +13183,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": +"ansi-colors@npm:4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 @@ -15826,7 +13197,7 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:^4.1.3": +"ansi-colors@npm:^4.1.1, ansi-colors@npm:^4.1.3": version: 4.1.3 resolution: "ansi-colors@npm:4.1.3" checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e @@ -16144,19 +13515,7 @@ __metadata: languageName: node linkType: hard -"args@npm:^5.0.1": - version: 5.0.1 - resolution: "args@npm:5.0.1" - dependencies: - camelcase: 5.0.0 - chalk: 2.4.2 - leven: 2.1.0 - mri: 1.1.4 - checksum: 51e2a05f32d15b8e292f000e6b232118df61b8f4fd446b17bb4e99df9ab47fe2c4a01924d7f967a6f08e82f9c19be277b08ed22bceff058aca849144ef8efed3 - languageName: node - linkType: hard - -"args@npm:^5.0.3": +"args@npm:^5.0.1, args@npm:^5.0.3": version: 5.0.3 resolution: "args@npm:5.0.3" dependencies: @@ -16661,24 +14020,7 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.0.3, babel-jest@npm:^29.5.0": - version: 29.5.0 - resolution: "babel-jest@npm:29.5.0" - dependencies: - "@jest/transform": ^29.5.0 - "@types/babel__core": ^7.1.14 - babel-plugin-istanbul: ^6.1.1 - babel-preset-jest: ^29.5.0 - chalk: ^4.0.0 - graceful-fs: ^4.2.9 - slash: ^3.0.0 - peerDependencies: - "@babel/core": ^7.8.0 - checksum: eafb6d37deb71f0c80bf3c80215aa46732153e5e8bcd73f6ff47d92e5c0c98c8f7f75995d0efec6289c371edad3693cd8fa2367b0661c4deb71a3a7117267ede - languageName: node - linkType: hard - -"babel-jest@npm:^29.6.1": +"babel-jest@npm:^29.0.3, babel-jest@npm:^29.5.0, babel-jest@npm:^29.6.1": version: 29.6.1 resolution: "babel-jest@npm:29.6.1" dependencies: @@ -16803,29 +14145,16 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.3.3": - version: 0.3.3 - resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" - dependencies: - "@babel/compat-data": ^7.17.7 - "@babel/helper-define-polyfill-provider": ^0.3.3 - semver: ^6.1.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7db3044993f3dddb3cc3d407bc82e640964a3bfe22de05d90e1f8f7a5cb71460011ab136d3c03c6c1ba428359ebf635688cd6205e28d0469bba221985f5c6179 - languageName: node - linkType: hard - -"babel-plugin-polyfill-corejs2@npm:^0.4.3": - version: 0.4.3 - resolution: "babel-plugin-polyfill-corejs2@npm:0.4.3" +"babel-plugin-polyfill-corejs2@npm:^0.4.4": + version: 0.4.5 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.5" dependencies: - "@babel/compat-data": ^7.17.7 - "@babel/helper-define-polyfill-provider": ^0.4.0 - semver: ^6.1.1 + "@babel/compat-data": ^7.22.6 + "@babel/helper-define-polyfill-provider": ^0.4.2 + semver: ^6.3.1 peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 09ba40b9f8ac66a733628b2f12722bb764bdcc4f9600b93d60f1994418a8f84bc4b1ed9ab07c9d288debbf6210413fdff0721a3a43bd89c7f77adf76b0310adc + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 33a8e06aa54e2858d211c743d179f0487b03222f9ca1bfd7c4865bca243fca942a3358cb75f6bb894ed476cbddede834811fbd6903ff589f055821146f053e1a languageName: node linkType: hard @@ -16841,49 +14170,26 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.6.0": - version: 0.6.0 - resolution: "babel-plugin-polyfill-corejs3@npm:0.6.0" - dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.3 - core-js-compat: ^3.25.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 470bb8c59f7c0912bd77fe1b5a2e72f349b3f65bbdee1d60d6eb7e1f4a085c6f24b2dd5ab4ac6c2df6444a96b070ef6790eccc9edb6a2668c60d33133bfb62c6 - languageName: node - linkType: hard - -"babel-plugin-polyfill-corejs3@npm:^0.8.1": - version: 0.8.1 - resolution: "babel-plugin-polyfill-corejs3@npm:0.8.1" - dependencies: - "@babel/helper-define-polyfill-provider": ^0.4.0 - core-js-compat: ^3.30.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c23a581973c141a4687126cf964981180ef27e3eb0b34b911161db4f5caf9ba7ff60bee0ebe46d650ba09e03a6a3ac2cd6a6ae5f4f5363a148470e5cd8447df2 - languageName: node - linkType: hard - -"babel-plugin-polyfill-regenerator@npm:^0.4.1": - version: 0.4.1 - resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" +"babel-plugin-polyfill-corejs3@npm:^0.8.2": + version: 0.8.3 + resolution: "babel-plugin-polyfill-corejs3@npm:0.8.3" dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.3 + "@babel/helper-define-polyfill-provider": ^0.4.2 + core-js-compat: ^3.31.0 peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ab0355efbad17d29492503230387679dfb780b63b25408990d2e4cf421012dae61d6199ddc309f4d2409ce4e9d3002d187702700dd8f4f8770ebbba651ed066c + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: dcbb30e551702a82cfd4d2c375da2c317658e55f95e9edcda93b9bbfdcc8fb6e5344efcb144e04d3406859e7682afce7974c60ededd9f12072a48a83dd22a0da languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.5.0": - version: 0.5.0 - resolution: "babel-plugin-polyfill-regenerator@npm:0.5.0" +"babel-plugin-polyfill-regenerator@npm:^0.5.1": + version: 0.5.2 + resolution: "babel-plugin-polyfill-regenerator@npm:0.5.2" dependencies: - "@babel/helper-define-polyfill-provider": ^0.4.0 + "@babel/helper-define-polyfill-provider": ^0.4.2 peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ef2bcffc7c9a5e4426fc2dbf89bf3a46999a8415c21cd741c3ab3cb4b5ab804aaa3d71ef733f0eda1bcc0b91d9d80f98d33983a66dab9b8bed166ec38f8f8ad1 + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: d962200f604016a9a09bc9b4aaf60a3db7af876bb65bcefaeac04d44ac9d9ec4037cf24ce117760cc141d7046b6394c7eb0320ba9665cb4a2ee64df2be187c93 languageName: node linkType: hard @@ -17345,7 +14651,7 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:1.20.2, body-parser@npm:^1.20.2": +"body-parser@npm:1.20.2, body-parser@npm:^1.19.0, body-parser@npm:^1.20.2": version: 1.20.2 resolution: "body-parser@npm:1.20.2" dependencies: @@ -17365,26 +14671,6 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:^1.19.0": - version: 1.20.0 - resolution: "body-parser@npm:1.20.0" - dependencies: - bytes: 3.1.2 - content-type: ~1.0.4 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.10.3 - raw-body: 2.5.1 - type-is: ~1.6.18 - unpipe: 1.0.0 - checksum: 12fffdeac82fe20dddcab7074215d5156e7d02a69ae90cbe9fee1ca3efa2f28ef52097cbea76685ee0a1509c71d85abd0056a08e612c09077cad6277a644cf88 - languageName: node - linkType: hard - "bonjour-service@npm:^1.0.11": version: 1.1.1 resolution: "bonjour-service@npm:1.1.1" @@ -17639,21 +14925,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.3": - version: 4.21.3 - resolution: "browserslist@npm:4.21.3" - dependencies: - caniuse-lite: ^1.0.30001370 - electron-to-chromium: ^1.4.202 - node-releases: ^2.0.6 - update-browserslist-db: ^1.0.5 - bin: - browserslist: cli.js - checksum: ff512a7bcca1c530e2854bbdfc7be2791d0fb524097a6340e56e1d5924164c7e4e0a9b070de04cdc4c149d15cb4d4275cb7c626ebbce954278a2823aaad2452a - languageName: node - linkType: hard - -"browserslist@npm:^4.21.5": +"browserslist@npm:^4.0.0, browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.9": version: 4.21.9 resolution: "browserslist@npm:4.21.9" dependencies: @@ -18160,14 +15432,7 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001370": - version: 1.0.30001464 - resolution: "caniuse-lite@npm:1.0.30001464" - checksum: 67cdee102c1660d62d7b9dbd4740bb7af096236618f2509fd2e0039d50db5f02fb87c21d90b6d573fdcf50deaf3c84503d009e871502b5c221d0ba1dec18ba11 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001503": +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001503": version: 1.0.30001503 resolution: "caniuse-lite@npm:1.0.30001503" checksum: cd5f0af37655ff71ec4ab3c49124d75e0b8b68de625d07ea80e9a82329e616b5203d5dad6865192653be9da50081c06878f081ab069dac0be35adf29aa1599cd @@ -18273,22 +15538,7 @@ __metadata: languageName: node linkType: hard -"chai@npm:>1.9.0": - version: 4.3.6 - resolution: "chai@npm:4.3.6" - dependencies: - assertion-error: ^1.1.0 - check-error: ^1.0.2 - deep-eql: ^3.0.1 - get-func-name: ^2.0.0 - loupe: ^2.3.1 - pathval: ^1.1.1 - type-detect: ^4.0.5 - checksum: acff93fd537f96d4a4d62dd83810285dffcfccb5089e1bf2a1205b28ec82d93dff551368722893cf85004282df10ee68802737c33c90c5493957ed449ed7ce71 - languageName: node - linkType: hard - -"chai@npm:^4.3.7": +"chai@npm:>1.9.0, chai@npm:^4.3.7": version: 4.3.7 resolution: "chai@npm:4.3.7" dependencies: @@ -18572,20 +15822,13 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^3.1.0": +"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 languageName: node linkType: hard -"ci-info@npm:^3.2.0": - version: 3.3.0 - resolution: "ci-info@npm:3.3.0" - checksum: c3d86fe374938ecda5093b1ba39acb535d8309185ba3f23587747c6a057e63f45419b406d880304dbc0e1d72392c9a33e42fe9a1e299209bc0ded5efaa232b66 - languageName: node - linkType: hard - "cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": version: 1.0.4 resolution: "cipher-base@npm:1.0.4" @@ -18990,14 +16233,7 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.10, colorette@npm:^2.0.14, colorette@npm:^2.0.7": - version: 2.0.19 - resolution: "colorette@npm:2.0.19" - checksum: 888cf5493f781e5fcf54ce4d49e9d7d698f96ea2b2ef67906834bb319a392c667f9ec69f4a10e268d2946d13a9503d2d19b3abaaaf174e3451bfe91fb9d82427 - languageName: node - linkType: hard - -"colorette@npm:^2.0.20": +"colorette@npm:^2.0.10, colorette@npm:^2.0.14, colorette@npm:^2.0.20, colorette@npm:^2.0.7": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d @@ -19275,14 +16511,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.4, content-type@npm:~1.0.4": - version: 1.0.4 - resolution: "content-type@npm:1.0.4" - checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0 - languageName: node - linkType: hard - -"content-type@npm:~1.0.5": +"content-type@npm:^1.0.4, content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 @@ -19390,21 +16619,12 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.25.1, core-js-compat@npm:^3.8.1": - version: 3.25.1 - resolution: "core-js-compat@npm:3.25.1" - dependencies: - browserslist: ^4.21.3 - checksum: 34dbec657adc2f660f4cd701709c9c5e27cbd608211c65df09458f80f3e357b9492ba1c5173e17cca72d889dcc6da01268cadf88fb407cf1726e76d301c6143e - languageName: node - linkType: hard - -"core-js-compat@npm:^3.30.1, core-js-compat@npm:^3.30.2": - version: 3.31.0 - resolution: "core-js-compat@npm:3.31.0" +"core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.8.1": + version: 3.31.1 + resolution: "core-js-compat@npm:3.31.1" dependencies: - browserslist: ^4.21.5 - checksum: 5c76ac5e4ab39480391f93a5aef14a2cfa188cda7bd6a7b8532de1f8bc5d89099a5025b2640d2ef70a2928614792363dcbcf8bd254aa7b2e11b85aeed7ac460f + browserslist: ^4.21.9 + checksum: 9a16d6992621f4e099169297381a28d5712cdef7df1fa85352a7c285a5885d5d7a117ec2eae9ad715ed88c7cc774787a22cdb8aceababf6775fbc8b0cbeccdb7 languageName: node linkType: hard @@ -19627,7 +16847,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:3.1.5, cross-fetch@npm:^3.1.5": +"cross-fetch@npm:3.1.5": version: 3.1.5 resolution: "cross-fetch@npm:3.1.5" dependencies: @@ -19636,7 +16856,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.0.4": +"cross-fetch@npm:^3.0.4, cross-fetch@npm:^3.1.5": version: 3.1.6 resolution: "cross-fetch@npm:3.1.6" dependencies: @@ -20430,14 +17650,7 @@ __metadata: languageName: node linkType: hard -"decode-uri-component@npm:^0.2.0": - version: 0.2.0 - resolution: "decode-uri-component@npm:0.2.0" - checksum: f3749344ab9305ffcfe4bfe300e2dbb61fc6359e2b736812100a3b1b6db0a5668cba31a05e4b45d4d63dbf1a18dfa354cd3ca5bb3ededddabb8cd293f4404f94 - languageName: node - linkType: hard - -"decode-uri-component@npm:^0.2.2": +"decode-uri-component@npm:^0.2.0, decode-uri-component@npm:^0.2.2": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 @@ -20532,15 +17745,6 @@ __metadata: languageName: node linkType: hard -"deep-eql@npm:^3.0.1": - version: 3.0.1 - resolution: "deep-eql@npm:3.0.1" - dependencies: - type-detect: ^4.0.0 - checksum: 4f4c9fb79eb994fb6e81d4aa8b063adc40c00f831588aa65e20857d5d52f15fb23034a6576ecf886f7ff6222d5ae42e71e9b7d57113e0715b1df7ea1e812b125 - languageName: node - linkType: hard - "deep-eql@npm:^4.1.2": version: 4.1.3 resolution: "deep-eql@npm:4.1.3" @@ -21034,14 +18238,7 @@ __metadata: languageName: node linkType: hard -"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": - version: 2.2.0 - resolution: "domelementtype@npm:2.2.0" - checksum: 24cb386198640cd58aa36f8c987f2ea61859929106d06ffcc8f547e70cb2ed82a6dc56dcb8252b21fba1f1ea07df6e4356d60bfe57f77114ca1aed6828362629 - languageName: node - linkType: hard - -"domelementtype@npm:^2.3.0": +"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": version: 2.3.0 resolution: "domelementtype@npm:2.3.0" checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 @@ -21307,13 +18504,6 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.202": - version: 1.4.249 - resolution: "electron-to-chromium@npm:1.4.249" - checksum: 830a35a157af7ae226f1528d727e369bb13f53bc7a4edefdf718651ace09d7d7b4bd7b70d33b5018b8eff6cf99ee58409b6c4140cd6d56350c1966f280ac5c93 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.4.431": version: 1.4.433 resolution: "electron-to-chromium@npm:1.4.433" @@ -21511,16 +18701,6 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.10.0": - version: 5.10.0 - resolution: "enhanced-resolve@npm:5.10.0" - dependencies: - graceful-fs: ^4.2.4 - tapable: ^2.2.0 - checksum: 0bb9830704db271610f900e8d79d70a740ea16f251263362b0c91af545576d09fe50103496606c1300a05e588372d6f9780a9bc2e30ce8ef9b827ec8f44687ff - languageName: node - linkType: hard - "enhanced-resolve@npm:^5.15.0": version: 5.15.0 resolution: "enhanced-resolve@npm:5.15.0" @@ -21554,20 +18734,13 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0": +"entities@npm:^4.2.0, entities@npm:^4.4.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 languageName: node linkType: hard -"entities@npm:^4.4.0": - version: 4.4.0 - resolution: "entities@npm:4.4.0" - checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2 - languageName: node - linkType: hard - "entities@npm:~2.0.0": version: 2.0.3 resolution: "entities@npm:2.0.3" @@ -21701,13 +18874,6 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^0.9.0": - version: 0.9.3 - resolution: "es-module-lexer@npm:0.9.3" - checksum: 84bbab23c396281db2c906c766af58b1ae2a1a2599844a504df10b9e8dc77ec800b3211fdaa133ff700f5703d791198807bba25d9667392d27a5e9feda344da8 - languageName: node - linkType: hard - "es-module-lexer@npm:^1.2.1": version: 1.3.0 resolution: "es-module-lexer@npm:1.3.0" @@ -21763,7 +18929,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.17.5": +"esbuild@npm:^0.17.5, esbuild@npm:^0.17.6": version: 0.17.19 resolution: "esbuild@npm:0.17.19" dependencies: @@ -21840,83 +19006,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.17.6": - version: 0.17.18 - resolution: "esbuild@npm:0.17.18" - dependencies: - "@esbuild/android-arm": 0.17.18 - "@esbuild/android-arm64": 0.17.18 - "@esbuild/android-x64": 0.17.18 - "@esbuild/darwin-arm64": 0.17.18 - "@esbuild/darwin-x64": 0.17.18 - "@esbuild/freebsd-arm64": 0.17.18 - "@esbuild/freebsd-x64": 0.17.18 - "@esbuild/linux-arm": 0.17.18 - "@esbuild/linux-arm64": 0.17.18 - "@esbuild/linux-ia32": 0.17.18 - "@esbuild/linux-loong64": 0.17.18 - "@esbuild/linux-mips64el": 0.17.18 - "@esbuild/linux-ppc64": 0.17.18 - "@esbuild/linux-riscv64": 0.17.18 - "@esbuild/linux-s390x": 0.17.18 - "@esbuild/linux-x64": 0.17.18 - "@esbuild/netbsd-x64": 0.17.18 - "@esbuild/openbsd-x64": 0.17.18 - "@esbuild/sunos-x64": 0.17.18 - "@esbuild/win32-arm64": 0.17.18 - "@esbuild/win32-ia32": 0.17.18 - "@esbuild/win32-x64": 0.17.18 - dependenciesMeta: - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 900b333f649fd89804216fb61fb5a0ffadc6dc37a2ec3b5981b588f72821676ea649a7c0ec785f0dbe6e774080b084c8af5f6ee7adbc1b138faf2a8c35e2c69c - languageName: node - linkType: hard - "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -22248,14 +19337,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0": - version: 3.4.0 - resolution: "eslint-visitor-keys@npm:3.4.0" - checksum: 33159169462d3989321a1ec1e9aaaf6a24cc403d5d347e9886d1b5bfe18ffa1be73bdc6203143a28a606b142b1af49787f33cff0d6d0813eb5f2e8d2e1a6043c - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^3.4.1": +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1": version: 3.4.1 resolution: "eslint-visitor-keys@npm:3.4.1" checksum: f05121d868202736b97de7d750847a328fcfa8593b031c95ea89425333db59676ac087fa905eba438d0a3c5769632f828187e0c1a0d271832a2153c1d3661c2c @@ -22319,9 +19401,9 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.12.0": - version: 8.44.0 - resolution: "eslint@npm:8.44.0" +"eslint@npm:~8.45.0": + version: 8.45.0 + resolution: "eslint@npm:8.45.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.4.0 @@ -22348,7 +19430,6 @@ __metadata: globals: ^13.19.0 graphemer: ^1.4.0 ignore: ^5.2.0 - import-fresh: ^3.0.0 imurmurhash: ^0.1.4 is-glob: ^4.0.0 is-path-inside: ^3.0.3 @@ -22360,60 +19441,10 @@ __metadata: natural-compare: ^1.4.0 optionator: ^0.9.3 strip-ansi: ^6.0.1 - strip-json-comments: ^3.1.0 text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: d06309ce4aafb9d27d558c8e5e5aa5cba3bbec3ce8ceccbc7d4b7a35f2b67fd40189159155553270e2e6febeb69bd8a3b60d6241c8f5ddc2ef1702ccbd328501 - languageName: node - linkType: hard - -"eslint@npm:^8.43.0, eslint@npm:~8.43.0": - version: 8.43.0 - resolution: "eslint@npm:8.43.0" - dependencies: - "@eslint-community/eslint-utils": ^4.2.0 - "@eslint-community/regexpp": ^4.4.0 - "@eslint/eslintrc": ^2.0.3 - "@eslint/js": 8.43.0 - "@humanwhocodes/config-array": ^0.11.10 - "@humanwhocodes/module-importer": ^1.0.1 - "@nodelib/fs.walk": ^1.2.8 - ajv: ^6.10.0 - chalk: ^4.0.0 - cross-spawn: ^7.0.2 - debug: ^4.3.2 - doctrine: ^3.0.0 - escape-string-regexp: ^4.0.0 - eslint-scope: ^7.2.0 - eslint-visitor-keys: ^3.4.1 - espree: ^9.5.2 - esquery: ^1.4.2 - esutils: ^2.0.2 - fast-deep-equal: ^3.1.3 - file-entry-cache: ^6.0.1 - find-up: ^5.0.0 - glob-parent: ^6.0.2 - globals: ^13.19.0 - graphemer: ^1.4.0 - ignore: ^5.2.0 - import-fresh: ^3.0.0 - imurmurhash: ^0.1.4 - is-glob: ^4.0.0 - is-path-inside: ^3.0.3 - js-yaml: ^4.1.0 - json-stable-stringify-without-jsonify: ^1.0.1 - levn: ^0.4.1 - lodash.merge: ^4.6.2 - minimatch: ^3.1.2 - natural-compare: ^1.4.0 - optionator: ^0.9.1 - strip-ansi: ^6.0.1 - strip-json-comments: ^3.1.0 - text-table: ^0.2.0 - bin: - eslint: bin/eslint.js - checksum: 55654ce00b0d128822b57526e40473d0497c7c6be3886afdc0b41b6b0dfbd34d0eae8159911b18451b4db51a939a0e6d2e117e847ae419086884fc3d4fe23c7c + checksum: 3e6dcce5cc43c5e301662db88ee26d1d188b22c177b9f104d7eefd1191236980bd953b3670fe2fac287114b26d7c5420ab48407d7ea1c3a446d6313c000009da languageName: node linkType: hard @@ -22428,17 +19459,6 @@ __metadata: languageName: node linkType: hard -"espree@npm:^9.5.2": - version: 9.5.2 - resolution: "espree@npm:9.5.2" - dependencies: - acorn: ^8.8.0 - acorn-jsx: ^5.3.2 - eslint-visitor-keys: ^3.4.1 - checksum: 6506289d6eb26471c0b383ee24fee5c8ae9d61ad540be956b3127be5ce3bf687d2ba6538ee5a86769812c7c552a9d8239e8c4d150f9ea056c6d5cbe8399c03c1 - languageName: node - linkType: hard - "espree@npm:^9.6.0": version: 9.6.0 resolution: "espree@npm:9.6.0" @@ -22546,14 +19566,7 @@ __metadata: languageName: node linkType: hard -"eventemitter2@npm:^6.3.1": - version: 6.4.5 - resolution: "eventemitter2@npm:6.4.5" - checksum: 84504f9cf0cc30205cdd46783fe9df3733435e5097f13070b678023110b5ef07847651808ae280cd94c42cd5976880211c7a40321a8ff8fa56f7c5f9c5c11960 - languageName: node - linkType: hard - -"eventemitter2@npm:^6.4.9": +"eventemitter2@npm:^6.3.1, eventemitter2@npm:^6.4.9": version: 6.4.9 resolution: "eventemitter2@npm:6.4.9" checksum: be59577c1e1c35509c7ba0e2624335c35bbcfd9485b8a977384c6cc6759341ea1a98d3cb9dbaa5cea4fff9b687e504504e3f9c2cc1674cf3bd8a43a7c74ea3eb @@ -22763,20 +19776,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0": - version: 29.5.0 - resolution: "expect@npm:29.5.0" - dependencies: - "@jest/expect-utils": ^29.5.0 - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.5.0 - jest-message-util: ^29.5.0 - jest-util: ^29.5.0 - checksum: 58f70b38693df6e5c6892db1bcd050f0e518d6f785175dc53917d4fa6a7359a048e5690e19ddcb96b65c4493881dd89a3dabdab1a84dfa55c10cdbdabf37b2d7 - languageName: node - linkType: hard - -"expect@npm:^29.6.1": +"expect@npm:^29.0.0, expect@npm:^29.6.1": version: 29.6.1 resolution: "expect@npm:29.6.1" dependencies: @@ -22972,20 +19972,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": - version: 3.2.11 - resolution: "fast-glob@npm:3.2.11" - dependencies: - "@nodelib/fs.stat": ^2.0.2 - "@nodelib/fs.walk": ^1.2.3 - glob-parent: ^5.1.2 - merge2: ^1.3.0 - micromatch: ^4.0.4 - checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 - languageName: node - linkType: hard - -"fast-glob@npm:^3.2.12": +"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" dependencies: @@ -23554,17 +20541,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9": - version: 1.15.1 - resolution: "follow-redirects@npm:1.15.1" - peerDependenciesMeta: - debug: - optional: true - checksum: 6aa4e3e3cdfa3b9314801a1cd192ba756a53479d9d8cca65bf4db3a3e8834e62139245cd2f9566147c8dfe2efff1700d3e6aefd103de4004a7b99985e71dd533 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.15.2": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.4, follow-redirects@npm:^1.14.7, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.2": version: 1.15.2 resolution: "follow-redirects@npm:1.15.2" peerDependenciesMeta: @@ -23843,13 +20820,6 @@ __metadata: languageName: node linkType: hard -"fs-monkey@npm:^1.0.3": - version: 1.0.3 - resolution: "fs-monkey@npm:1.0.3" - checksum: cf50804833f9b88a476911ae911fe50f61a98d986df52f890bd97e7262796d023698cb2309fa9b74fdd8974f04315b648748a0a8ee059e7d5257b293bfc409c0 - languageName: node - linkType: hard - "fs-monkey@npm:^1.0.4": version: 1.0.4 resolution: "fs-monkey@npm:1.0.4" @@ -24701,14 +21671,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da - languageName: node - linkType: hard - -"graceful-fs@npm:^4.1.5": +"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 @@ -27420,18 +24383,6 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-diff@npm:29.5.0" - dependencies: - chalk: ^4.0.0 - diff-sequences: ^29.4.3 - jest-get-type: ^29.4.3 - pretty-format: ^29.5.0 - checksum: dfd0f4a299b5d127779c76b40106c37854c89c3e0785098c717d52822d6620d227f6234c3a9291df204d619e799e3654159213bf93220f79c8e92a55475a3d39 - languageName: node - linkType: hard - "jest-diff@npm:^29.6.1": version: 29.6.1 resolution: "jest-diff@npm:29.6.1" @@ -27550,29 +24501,6 @@ __metadata: languageName: node linkType: hard -"jest-haste-map@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-haste-map@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@types/graceful-fs": ^4.1.3 - "@types/node": "*" - anymatch: ^3.0.3 - fb-watchman: ^2.0.0 - fsevents: ^2.3.2 - graceful-fs: ^4.2.9 - jest-regex-util: ^29.4.3 - jest-util: ^29.5.0 - jest-worker: ^29.5.0 - micromatch: ^4.0.4 - walker: ^1.0.8 - dependenciesMeta: - fsevents: - optional: true - checksum: 3828ff7783f168e34be2c63887f82a01634261f605dcae062d83f979a61c37739e21b9607ecb962256aea3fbe5a530a1acee062d0026fcb47c607c12796cf3b7 - languageName: node - linkType: hard - "jest-haste-map@npm:^29.6.1": version: 29.6.1 resolution: "jest-haste-map@npm:29.6.1" @@ -27606,18 +24534,6 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-matcher-utils@npm:29.5.0" - dependencies: - chalk: ^4.0.0 - jest-diff: ^29.5.0 - jest-get-type: ^29.4.3 - pretty-format: ^29.5.0 - checksum: 1d3e8c746e484a58ce194e3aad152eff21fd0896e8b8bf3d4ab1a4e2cbfed95fb143646f4ad9fdf6e42212b9e8fc033268b58e011b044a9929df45485deb5ac9 - languageName: node - linkType: hard - "jest-matcher-utils@npm:^29.6.1": version: 29.6.1 resolution: "jest-matcher-utils@npm:29.6.1" @@ -27630,23 +24546,6 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-message-util@npm:29.5.0" - dependencies: - "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.5.0 - "@types/stack-utils": ^2.0.0 - chalk: ^4.0.0 - graceful-fs: ^4.2.9 - micromatch: ^4.0.4 - pretty-format: ^29.5.0 - slash: ^3.0.0 - stack-utils: ^2.0.3 - checksum: daddece6bbf846eb6a2ab9be9f2446e54085bef4e5cecd13d2a538fa9c01cb89d38e564c6b74fd8e12d37ed9eface8a362240ae9f21d68b214590631e7a0d8bf - languageName: node - linkType: hard - "jest-message-util@npm:^29.6.1": version: 29.6.1 resolution: "jest-message-util@npm:29.6.1" @@ -27850,21 +24749,7 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^29.0.0, jest-util@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-util@npm:29.5.0" - dependencies: - "@jest/types": ^29.5.0 - "@types/node": "*" - chalk: ^4.0.0 - ci-info: ^3.2.0 - graceful-fs: ^4.2.9 - picomatch: ^2.2.3 - checksum: fd9212950d34d2ecad8c990dda0d8ea59a8a554b0c188b53ea5d6c4a0829a64f2e1d49e6e85e812014933d17426d7136da4785f9cf76fff1799de51b88bc85d3 - languageName: node - linkType: hard - -"jest-util@npm:^29.6.1": +"jest-util@npm:^29.0.0, jest-util@npm:^29.6.1": version: 29.6.1 resolution: "jest-util@npm:29.6.1" dependencies: @@ -27940,18 +24825,6 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-worker@npm:29.5.0" - dependencies: - "@types/node": "*" - jest-util: ^29.5.0 - merge-stream: ^2.0.0 - supports-color: ^8.0.0 - checksum: 1151a1ae3602b1ea7c42a8f1efe2b5a7bf927039deaa0827bf978880169899b705744e288f80a63603fb3fc2985e0071234986af7dc2c21c7a64333d8777c7c9 - languageName: node - linkType: hard - "jest-worker@npm:^29.6.1": version: 29.6.1 resolution: "jest-worker@npm:29.6.1" @@ -29674,16 +26547,7 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.1.2, memfs@npm:^3.4.3": - version: 3.5.0 - resolution: "memfs@npm:3.5.0" - dependencies: - fs-monkey: ^1.0.3 - checksum: 8427db6c3644eeb9119b7a74b232d9a6178d018878acce6f05bd89d95e28b1073c9eeb00127131b0613b07a003e2e7b15b482f9004e548fe06a0aba7aa02515c - languageName: node - linkType: hard - -"memfs@npm:^3.2.2": +"memfs@npm:^3.1.2, memfs@npm:^3.2.2, memfs@npm:^3.4.3": version: 3.5.3 resolution: "memfs@npm:3.5.3" dependencies: @@ -30523,16 +27387,7 @@ __metadata: languageName: node linkType: hard -"moment-timezone@npm:*, moment-timezone@npm:^0.5.x": - version: 0.5.40 - resolution: "moment-timezone@npm:0.5.40" - dependencies: - moment: ">= 2.9.0" - checksum: 6f6be5412b37fd937bb143efe74bf65b2c3f115fd967a6dc13b717a126ed6dd198bff6db6e179d69a089e20ac03ce7622c6b5598dd585005195554487a91b528 - languageName: node - linkType: hard - -"moment-timezone@npm:^0.5.43, moment-timezone@npm:~0.5.43": +"moment-timezone@npm:*, moment-timezone@npm:^0.5.43, moment-timezone@npm:^0.5.x, moment-timezone@npm:~0.5.43": version: 0.5.43 resolution: "moment-timezone@npm:0.5.43" dependencies: @@ -30541,7 +27396,7 @@ __metadata: languageName: node linkType: hard -"moment@npm:>= 2.9.0, moment@npm:^2.10.2, moment@npm:^2.29.1, moment@npm:^2.29.4": +"moment@npm:^2.10.2, moment@npm:^2.29.1, moment@npm:^2.29.4": version: 2.29.4 resolution: "moment@npm:2.29.4" checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e @@ -30789,16 +27644,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.1, nanoid@npm:^3.3.4": - version: 3.3.4 - resolution: "nanoid@npm:3.3.4" - bin: - nanoid: bin/nanoid.cjs - checksum: 2fddd6dee994b7676f008d3ffa4ab16035a754f4bb586c61df5a22cf8c8c94017aadd360368f47d653829e0569a92b129979152ff97af23a558331e47e37cd9c - languageName: node - linkType: hard - -"nanoid@npm:^3.3.6": +"nanoid@npm:^3.3.1, nanoid@npm:^3.3.6": version: 3.3.6 resolution: "nanoid@npm:3.3.6" bin: @@ -31061,7 +27907,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:2.6.7, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": +"node-fetch@npm:2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" dependencies: @@ -31075,7 +27921,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.11": +"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.11, node-fetch@npm:^2.6.7": version: 2.6.11 resolution: "node-fetch@npm:2.6.11" dependencies: @@ -31219,13 +28065,6 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.6": - version: 2.0.6 - resolution: "node-releases@npm:2.0.6" - checksum: e86a926dc9fbb3b41b4c4a89d998afdf140e20a4e8dbe6c0a807f7b2948b42ea97d7fd3ad4868041487b6e9ee98409829c6e4d84a734a4215dff060a7fbeb4bf - languageName: node - linkType: hard - "node-rsa@npm:^1.1.1": version: 1.1.1 resolution: "node-rsa@npm:1.1.1" @@ -31902,21 +28741,7 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.9.1": - version: 0.9.1 - resolution: "optionator@npm:0.9.1" - dependencies: - deep-is: ^0.1.3 - fast-levenshtein: ^2.0.6 - levn: ^0.4.1 - prelude-ls: ^1.2.1 - type-check: ^0.4.0 - word-wrap: ^1.2.3 - checksum: dbc6fa065604b24ea57d734261914e697bd73b69eff7f18e967e8912aa2a40a19a9f599a507fa805be6c13c24c4eae8c71306c239d517d42d4c041c942f508a0 - languageName: node - linkType: hard - -"optionator@npm:^0.9.3": +"optionator@npm:^0.9.1, optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" dependencies: @@ -33743,17 +30568,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.0, postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.6": - version: 6.0.10 - resolution: "postcss-selector-parser@npm:6.0.10" - dependencies: - cssesc: ^3.0.0 - util-deprecate: ^1.0.2 - checksum: 46afaa60e3d1998bd7adf6caa374baf857cc58d3ff944e29459c9a9e4680a7fe41597bd5b755fc81d7c388357e9bf67c0251d047c640a09f148e13606b8a8608 - languageName: node - linkType: hard - -"postcss-selector-parser@npm:^6.0.4": +"postcss-selector-parser@npm:^6.0.0, postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.6": version: 6.0.13 resolution: "postcss-selector-parser@npm:6.0.13" dependencies: @@ -33854,7 +30669,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.2.15, postcss@npm:~8.4.24": +"postcss@npm:^8.2.15, postcss@npm:^8.3.11, postcss@npm:^8.4.14, postcss@npm:^8.4.23, postcss@npm:~8.4.24": version: 8.4.24 resolution: "postcss@npm:8.4.24" dependencies: @@ -33865,28 +30680,6 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.3.11, postcss@npm:^8.4.14": - version: 8.4.16 - resolution: "postcss@npm:8.4.16" - dependencies: - nanoid: ^3.3.4 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: 10eee25efd77868036403858577da0cefaf2e0905feeaba5770d5438ccdddba3d01cba8063e96b8aac4c6daa0ed413dd5ae0554a433a3c4db38df1d134cffc1f - languageName: node - linkType: hard - -"postcss@npm:^8.4.23": - version: 8.4.23 - resolution: "postcss@npm:8.4.23" - dependencies: - nanoid: ^3.3.6 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: 8bb9d1b2ea6e694f8987d4f18c94617971b2b8d141602725fedcc2222fdc413b776a6e1b969a25d627d7b2681ca5aabb56f59e727ef94072e1b6ac8412105a2f - languageName: node - linkType: hard - "postis@npm:^2.2.0": version: 2.2.0 resolution: "postis@npm:2.2.0" @@ -34063,18 +30856,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.5.0": - version: 29.5.0 - resolution: "pretty-format@npm:29.5.0" - dependencies: - "@jest/schemas": ^29.4.3 - ansi-styles: ^5.0.0 - react-is: ^18.0.0 - checksum: 4065356b558e6db25b4d41a01efb386935a6c06a0c9c104ef5ce59f2f476b8210edb8b3949b386e60ada0a6dc5ebcb2e6ccddc8c64dfd1a9943c3c3a9e7eaf89 - languageName: node - linkType: hard - -"pretty-format@npm:^29.6.1": +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.6.1": version: 29.6.1 resolution: "pretty-format@npm:29.6.1" dependencies: @@ -34467,15 +31249,6 @@ __metadata: languageName: node linkType: hard -"qs@npm:6.10.3": - version: 6.10.3 - resolution: "qs@npm:6.10.3" - dependencies: - side-channel: ^1.0.4 - checksum: 0fac5e6c7191d0295a96d0e83c851aeb015df7e990e4d3b093897d3ac6c94e555dbd0a599739c84d7fa46d7fee282d94ba76943983935cf33bba6769539b8019 - languageName: node - linkType: hard - "qs@npm:6.11.0, qs@npm:^6.10.0, qs@npm:^6.10.3, qs@npm:^6.7.0, qs@npm:^6.9.4, qs@npm:^6.9.6": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -34771,19 +31544,7 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.5.1, raw-body@npm:^2.2.0": - version: 2.5.1 - resolution: "raw-body@npm:2.5.1" - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e - languageName: node - linkType: hard - -"raw-body@npm:2.5.2": +"raw-body@npm:2.5.2, raw-body@npm:^2.2.0": version: 2.5.2 resolution: "raw-body@npm:2.5.2" dependencies: @@ -34807,20 +31568,7 @@ __metadata: languageName: node linkType: hard -"rc-scrollbars@npm:^1.1.5": - version: 1.1.5 - resolution: "rc-scrollbars@npm:1.1.5" - dependencies: - dom-css: ^2.1.0 - raf: ^3.4.1 - peerDependencies: - react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: d2b3710868f70ed7c154768bd03825b543d7804ef1c95086ee9cd851f66a046e1b4dbc9e153f9797e1706a11d263db4a645eff485d16bf1c095754e6e771f5ad - languageName: node - linkType: hard - -"rc-scrollbars@npm:^1.1.6": +"rc-scrollbars@npm:^1.1.5, rc-scrollbars@npm:^1.1.6": version: 1.1.6 resolution: "rc-scrollbars@npm:1.1.6" dependencies: @@ -36310,27 +33058,13 @@ __metadata: sodium-native: ^3.3.0 sodium-plus: ^0.9.0 ts-node: ^10.9.1 - typescript: ~5.1.3 + typescript: ~5.1.6 uuid: ^8.3.2 ws: ^8.8.1 languageName: unknown linkType: soft -"rollup@npm:^3.2.5": - version: 3.21.3 - resolution: "rollup@npm:3.21.3" - dependencies: - fsevents: ~2.3.2 - dependenciesMeta: - fsevents: - optional: true - bin: - rollup: dist/bin/rollup - checksum: 430c0c66f1480586ccea77b190524efb6a3af3da5308c7bc77eca3160c3f387c9a56ebb3eef5bfb24f683764bdfdd3dafdea175d215e4b875bbde752619bec12 - languageName: node - linkType: hard - -"rollup@npm:^3.21.0": +"rollup@npm:^3.2.5, rollup@npm:^3.21.0": version: 3.23.0 resolution: "rollup@npm:3.23.0" dependencies: @@ -36638,18 +33372,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": - version: 3.1.1 - resolution: "schema-utils@npm:3.1.1" - dependencies: - "@types/json-schema": ^7.0.8 - ajv: ^6.12.5 - ajv-keywords: ^3.5.2 - checksum: fb73f3d759d43ba033c877628fe9751620a26879f6301d3dbeeb48cf2a65baec5cdf99da65d1bf3b4ff5444b2e59cbe4f81c2456b5e0d2ba7d7fd4aed5da29ce - languageName: node - linkType: hard - -"schema-utils@npm:^3.2.0": +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" dependencies: @@ -36778,7 +33501,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:6.3.0, semver@npm:^6.0.0, semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.3.0": +"semver@npm:6.3.0": version: 6.3.0 resolution: "semver@npm:6.3.0" bin: @@ -36787,36 +33510,23 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.2, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": - version: 7.3.7 - resolution: "semver@npm:7.3.7" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 - languageName: node - linkType: hard - -"semver@npm:^7.5.2": - version: 7.5.2 - resolution: "semver@npm:7.5.2" +"semver@npm:7.x, semver@npm:^7.2, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3": + version: 7.5.3 + resolution: "semver@npm:7.5.3" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: 3fdf5d1e6f170fe8bcc41669e31787649af91af7f54f05c71d0865bb7aa27e8b92f68b3e6b582483e2c1c648008bc84249d2cd86301771fe5cbf7621d1fe5375 + checksum: 9d58db16525e9f749ad0a696a1f27deabaa51f66e91d2fa2b0db3de3e9644e8677de3b7d7a03f4c15bc81521e0c3916d7369e0572dbde250d9bedf5194e2a8a7 languageName: node linkType: hard -"semver@npm:^7.5.3": - version: 7.5.3 - resolution: "semver@npm:7.5.3" - dependencies: - lru-cache: ^6.0.0 +"semver@npm:^6.0.0, semver@npm:^6.1.2, semver@npm:^6.3.0, semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" bin: semver: bin/semver.js - checksum: 9d58db16525e9f749ad0a696a1f27deabaa51f66e91d2fa2b0db3de3e9644e8677de3b7d7a03f4c15bc81521e0c3916d7369e0572dbde250d9bedf5194e2a8a7 + checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 languageName: node linkType: hard @@ -36870,7 +33580,7 @@ __metadata: languageName: node linkType: hard -"serialize-javascript@npm:6.0.0, serialize-javascript@npm:^6.0.0": +"serialize-javascript@npm:6.0.0": version: 6.0.0 resolution: "serialize-javascript@npm:6.0.0" dependencies: @@ -38898,28 +35608,6 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.1.3": - version: 5.3.5 - resolution: "terser-webpack-plugin@npm:5.3.5" - dependencies: - "@jridgewell/trace-mapping": ^0.3.14 - jest-worker: ^27.4.5 - schema-utils: ^3.1.1 - serialize-javascript: ^6.0.0 - terser: ^5.14.1 - peerDependencies: - webpack: ^5.1.0 - peerDependenciesMeta: - "@swc/core": - optional: true - esbuild: - optional: true - uglify-js: - optional: true - checksum: 611c7b38d6fa0213dc03f48da9efe29c7edd098fc128a64905f7c9b61af8e7c36c13113d46b50be19ee2b8378442f4e1b8b4ddac9bba2cb73499ed32fc0e18f4 - languageName: node - linkType: hard - "terser@npm:^4.1.2, terser@npm:^4.6.3": version: 4.8.0 resolution: "terser@npm:4.8.0" @@ -38933,7 +35621,7 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.10.0, terser@npm:^5.16.8": +"terser@npm:^5.10.0, terser@npm:^5.16.8, terser@npm:^5.3.4": version: 5.18.0 resolution: "terser@npm:5.18.0" dependencies: @@ -38947,20 +35635,6 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.14.1, terser@npm:^5.3.4": - version: 5.14.2 - resolution: "terser@npm:5.14.2" - dependencies: - "@jridgewell/source-map": ^0.3.2 - acorn: ^8.5.0 - commander: ^2.20.0 - source-map-support: ~0.5.20 - bin: - terser: bin/terser - checksum: cabb50a640d6c2cfb351e4f43dc7bf7436f649755bb83eb78b2cacda426d5e0979bd44e6f92d713f3ca0f0866e322739b9ced888ebbce6508ad872d08de74fcc - languageName: node - linkType: hard - "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -39127,20 +35801,13 @@ __metadata: languageName: node linkType: hard -"tiny-invariant@npm:^1.0.6": +"tiny-invariant@npm:^1.0.6, tiny-invariant@npm:^1.2.0": version: 1.3.1 resolution: "tiny-invariant@npm:1.3.1" checksum: 872dbd1ff20a21303a2fd20ce3a15602cfa7fcf9b228bd694a52e2938224313b5385a1078cb667ed7375d1612194feaca81c4ecbe93121ca1baebe344de4f84c languageName: node linkType: hard -"tiny-invariant@npm:^1.2.0": - version: 1.2.0 - resolution: "tiny-invariant@npm:1.2.0" - checksum: e09a718a7c4a499ba592cdac61f015d87427a0867ca07f50c11fd9b623f90cdba18937b515d4a5e4f43dac92370498d7bdaee0d0e7a377a61095e02c4a92eade - languageName: node - linkType: hard - "tinykeys@npm:^1.4.0": version: 1.4.0 resolution: "tinykeys@npm:1.4.0" @@ -39563,14 +36230,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": - version: 2.5.0 - resolution: "tslib@npm:2.5.0" - checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 - languageName: node - linkType: hard - -"tslib@npm:^2.5.3": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.3": version: 2.5.3 resolution: "tslib@npm:2.5.3" checksum: 88902b309afaf83259131c1e13da1dceb0ad1682a213143a1346a649143924d78cf3760c448b84d796938fd76127183894f8d85cbb3bf9c4fddbfcc140c0003c @@ -39678,58 +36338,58 @@ __metadata: languageName: node linkType: hard -"turbo-darwin-64@npm:1.10.8": - version: 1.10.8 - resolution: "turbo-darwin-64@npm:1.10.8" +"turbo-darwin-64@npm:1.10.9": + version: 1.10.9 + resolution: "turbo-darwin-64@npm:1.10.9" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"turbo-darwin-arm64@npm:1.10.8": - version: 1.10.8 - resolution: "turbo-darwin-arm64@npm:1.10.8" +"turbo-darwin-arm64@npm:1.10.9": + version: 1.10.9 + resolution: "turbo-darwin-arm64@npm:1.10.9" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"turbo-linux-64@npm:1.10.8": - version: 1.10.8 - resolution: "turbo-linux-64@npm:1.10.8" +"turbo-linux-64@npm:1.10.9": + version: 1.10.9 + resolution: "turbo-linux-64@npm:1.10.9" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"turbo-linux-arm64@npm:1.10.8": - version: 1.10.8 - resolution: "turbo-linux-arm64@npm:1.10.8" +"turbo-linux-arm64@npm:1.10.9": + version: 1.10.9 + resolution: "turbo-linux-arm64@npm:1.10.9" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"turbo-windows-64@npm:1.10.8": - version: 1.10.8 - resolution: "turbo-windows-64@npm:1.10.8" +"turbo-windows-64@npm:1.10.9": + version: 1.10.9 + resolution: "turbo-windows-64@npm:1.10.9" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"turbo-windows-arm64@npm:1.10.8": - version: 1.10.8 - resolution: "turbo-windows-arm64@npm:1.10.8" +"turbo-windows-arm64@npm:1.10.9": + version: 1.10.9 + resolution: "turbo-windows-arm64@npm:1.10.9" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard "turbo@npm:latest": - version: 1.10.8 - resolution: "turbo@npm:1.10.8" - dependencies: - turbo-darwin-64: 1.10.8 - turbo-darwin-arm64: 1.10.8 - turbo-linux-64: 1.10.8 - turbo-linux-arm64: 1.10.8 - turbo-windows-64: 1.10.8 - turbo-windows-arm64: 1.10.8 + version: 1.10.9 + resolution: "turbo@npm:1.10.9" + dependencies: + turbo-darwin-64: 1.10.9 + turbo-darwin-arm64: 1.10.9 + turbo-linux-64: 1.10.9 + turbo-linux-arm64: 1.10.9 + turbo-windows-64: 1.10.9 + turbo-windows-arm64: 1.10.9 dependenciesMeta: turbo-darwin-64: optional: true @@ -39745,7 +36405,7 @@ __metadata: optional: true bin: turbo: bin/turbo - checksum: 9fd2f9b17461a688e200329c4d910969e828b22c375e88759cd0e59f96db7aac115d289ed20af42db23030b4a4ae0334d1a37bf0a5caf2fdc8a618476742238f + checksum: 667867c3ea6ecf59cafe494e1bd4da8564bdae4ea735a14813656b2784a909fb69f12f8c6cdb140ca490c602fd11dc186b5d426af6a451ba51a487e73b742731 languageName: node linkType: hard @@ -39925,43 +36585,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.1.3, typescript@npm:~5.1.3": - version: 5.1.3 - resolution: "typescript@npm:5.1.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: d9d51862d98efa46534f2800a1071a613751b1585dc78884807d0c179bcd93d6e9d4012a508e276742f5f33c480adefc52ffcafaf9e0e00ab641a14cde9a31c7 - languageName: node - linkType: hard - -"typescript@npm:~5.0.2": - version: 5.0.4 - resolution: "typescript@npm:5.0.4" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 82b94da3f4604a8946da585f7d6c3025fff8410779e5bde2855ab130d05e4fd08938b9e593b6ebed165bda6ad9292b230984f10952cf82f0a0ca07bbeaa08172 - languageName: node - linkType: hard - -"typescript@patch:typescript@^5.1.3#~builtin<compat/typescript>, typescript@patch:typescript@~5.1.3#~builtin<compat/typescript>": - version: 5.1.3 - resolution: "typescript@patch:typescript@npm%3A5.1.3#~builtin<compat/typescript>::version=5.1.3&hash=f456af" +"typescript@npm:~5.1.6": + version: 5.1.6 + resolution: "typescript@npm:5.1.6" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 32a25b2e128a4616f999d4ee502aabb1525d5647bc8955e6edf05d7fbc53af8aa98252e2f6ba80bcedfc0260c982b885f3c09cfac8bb65d2924f3133ad1e1e62 + checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 languageName: node linkType: hard -"typescript@patch:typescript@~5.0.2#~builtin<compat/typescript>": - version: 5.0.4 - resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin<compat/typescript>::version=5.0.4&hash=f456af" +"typescript@patch:typescript@~5.1.6#~builtin<compat/typescript>": + version: 5.1.6 + resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin<compat/typescript>::version=5.1.6&hash=f456af" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 6a1fe9a77bb9c5176ead919cc4a1499ee63e46b4e05bf667079f11bf3a8f7887f135aa72460a4c3b016e6e6bb65a822cb8689a6d86cbfe92d22cc9f501f09213 + checksum: 21e88b0a0c0226f9cb9fd25b9626fb05b4c0f3fddac521844a13e1f30beb8f14e90bd409a9ac43c812c5946d714d6e0dee12d5d02dfc1c562c5aacfa1f49b606 languageName: node linkType: hard @@ -40370,20 +37010,6 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.5": - version: 1.0.9 - resolution: "update-browserslist-db@npm:1.0.9" - dependencies: - escalade: ^3.1.1 - picocolors: ^1.0.0 - peerDependencies: - browserslist: ">= 4.21.0" - bin: - browserslist-lint: cli.js - checksum: f625899b236f6a4d7f62b56be1b8da230c5563d1fef84d3ef148f2e1a3f11a5a4b3be4fd7e3703e51274c116194017775b10afb4de09eb2c0d09d36b90f1f578 - languageName: node - linkType: hard - "update-check@npm:1.5.2": version: 1.5.2 resolution: "update-check@npm:1.5.2" @@ -40946,7 +37572,7 @@ __metadata: languageName: node linkType: hard -"vm2@npm:^3.9.19": +"vm2@npm:^3.9.19, vm2@npm:^3.9.8": version: 3.9.19 resolution: "vm2@npm:3.9.19" dependencies: @@ -40958,18 +37584,6 @@ __metadata: languageName: node linkType: hard -"vm2@npm:^3.9.8": - version: 3.9.17 - resolution: "vm2@npm:3.9.17" - dependencies: - acorn: ^8.7.0 - acorn-walk: ^8.2.0 - bin: - vm2: bin/vm2 - checksum: 9a03740a40ab2be5e3348a95fb31512da1a3c85318febb07e5299fa103ff05bcd7b6f458211fa38a1281dc27beccd04ff90355fc1d34fe2ee6ca10d0bb8c6f35 - languageName: node - linkType: hard - "void-elements@npm:3.1.0": version: 3.1.0 resolution: "void-elements@npm:3.1.0" @@ -41403,7 +38017,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:>=4.0.0 <6.0.0": +"webpack@npm:>=4.0.0 <6.0.0, webpack@npm:>=4.43.0 <6.0.0, webpack@npm:^5.9.0": version: 5.88.0 resolution: "webpack@npm:5.88.0" dependencies: @@ -41440,80 +38054,6 @@ __metadata: languageName: node linkType: hard -"webpack@npm:>=4.43.0 <6.0.0": - version: 5.74.0 - resolution: "webpack@npm:5.74.0" - dependencies: - "@types/eslint-scope": ^3.7.3 - "@types/estree": ^0.0.51 - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/wasm-edit": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - acorn: ^8.7.1 - acorn-import-assertions: ^1.7.6 - browserslist: ^4.14.5 - chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.10.0 - es-module-lexer: ^0.9.0 - eslint-scope: 5.1.1 - events: ^3.2.0 - glob-to-regexp: ^0.4.1 - graceful-fs: ^4.2.9 - json-parse-even-better-errors: ^2.3.1 - loader-runner: ^4.2.0 - mime-types: ^2.1.27 - neo-async: ^2.6.2 - schema-utils: ^3.1.0 - tapable: ^2.1.1 - terser-webpack-plugin: ^5.1.3 - watchpack: ^2.4.0 - webpack-sources: ^3.2.3 - peerDependenciesMeta: - webpack-cli: - optional: true - bin: - webpack: bin/webpack.js - checksum: 320c41369a75051b19e18c63f408b3dcc481852e992f83d311771c5ec0f05f2946385e8ebef62030cf3587f0a3d2f12779ffdb191569a966847289ba7313f946 - languageName: node - linkType: hard - -"webpack@npm:^5.9.0": - version: 5.87.0 - resolution: "webpack@npm:5.87.0" - dependencies: - "@types/eslint-scope": ^3.7.3 - "@types/estree": ^1.0.0 - "@webassemblyjs/ast": ^1.11.5 - "@webassemblyjs/wasm-edit": ^1.11.5 - "@webassemblyjs/wasm-parser": ^1.11.5 - acorn: ^8.7.1 - acorn-import-assertions: ^1.9.0 - browserslist: ^4.14.5 - chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.15.0 - es-module-lexer: ^1.2.1 - eslint-scope: 5.1.1 - events: ^3.2.0 - glob-to-regexp: ^0.4.1 - graceful-fs: ^4.2.9 - json-parse-even-better-errors: ^2.3.1 - loader-runner: ^4.2.0 - mime-types: ^2.1.27 - neo-async: ^2.6.2 - schema-utils: ^3.2.0 - tapable: ^2.1.1 - terser-webpack-plugin: ^5.3.7 - watchpack: ^2.4.0 - webpack-sources: ^3.2.3 - peerDependenciesMeta: - webpack-cli: - optional: true - bin: - webpack: bin/webpack.js - checksum: b7d0e390f9d30627e303d54b17cb87b62f49ecffe2d35481f830679904993bae208e23748ffe0e6091b6dd4810562b2f2e88bb0f23b96515d74fb1e3c2898210 - languageName: node - linkType: hard - "websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": version: 0.7.4 resolution: "websocket-driver@npm:0.7.4" @@ -41763,7 +38303,7 @@ __metadata: languageName: node linkType: hard -"word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3": +"word-wrap@npm:~1.2.3": version: 1.2.3 resolution: "word-wrap@npm:1.2.3" checksum: 30b48f91fcf12106ed3186ae4fa86a6a1842416df425be7b60485de14bec665a54a68e4b5156647dec3a70f25e84d270ca8bc8cd23182ed095f5c7206a938c1f @@ -42160,7 +38700,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.0.0, yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": +"yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c @@ -42213,22 +38753,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1": - version: 17.5.1 - resolution: "yargs@npm:17.5.1" - dependencies: - cliui: ^7.0.2 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.3 - y18n: ^5.0.5 - yargs-parser: ^21.0.0 - checksum: 00d58a2c052937fa044834313f07910fd0a115dec5ee35919e857eeee3736b21a4eafa8264535800ba8bac312991ce785ecb8a51f4d2cc8c4676d865af1cfbde - languageName: node - linkType: hard - -"yargs@npm:^17.7.1": +"yargs@npm:^17.3.1, yargs@npm:^17.7.1": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 5f7f27547f1442554db2403c51214f87e3998267 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Sat, 22 Jul 2023 01:41:31 -0300 Subject: [PATCH 145/149] ci: adjust `.eslintignore` file (#29892) --- apps/meteor/.eslintignore | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/apps/meteor/.eslintignore b/apps/meteor/.eslintignore index 158360ed08eb..6ea7c6a55189 100644 --- a/apps/meteor/.eslintignore +++ b/apps/meteor/.eslintignore @@ -1,20 +1,12 @@ -node_modules -data/ -tests/e2e/test-failures/ -packages/autoupdate/ -packages/meteor-streams/ -app/emoji-emojione/generateEmojiIndex.js -packages/rocketchat-livechat/assets/rocketchat-livechat.min.js -packages/rocketchat-livechat/assets/rocket-livechat.js -app/theme/client/vendor/ -public/packages/rocketchat_videobridge/client/public/external_api.js -packages/tap-i18n/lib/tap_i18next/tap_i18next-1.7.3.js -private/moment-locales/ -public/livechat/ -public/pdf.worker.min.js -public/workers/**/* -imports/client/**/* -ee/server/services/dist/** +/node_modules/ +/tests/e2e/ +/tests/data/ +/packages/ +/app/emoji-emojione/generateEmojiIndex.js +/public/ +/private/moment-locales/ +/imports/ +/ee/server/services/dist/ !/.mocharc.js !/.mocharc.*.js !/.scripts/ From f9a748526d1d228a361eaaee97f43eef3b552ca8 Mon Sep 17 00:00:00 2001 From: csuadev <72958726+csuadev@users.noreply.github.com> Date: Sat, 22 Jul 2023 10:09:00 +0200 Subject: [PATCH 146/149] feat: Adding new UIKit components (#29566) Co-authored-by: Tiago Evangelista Pinto <17487063+tiagoevanp@users.noreply.github.com> --- .changeset/loud-sheep-try.md | 6 + .../src/blocks/CalloutBlock.tsx | 20 ++++ .../src/contexts/kitContext.ts | 4 +- .../src/elements/CheckboxElement.tsx | 45 ++++++++ .../src/elements/RadioButtonElement.tsx | 43 +++++++ .../src/elements/TimePickerElement.tsx | 38 +++++++ .../src/elements/ToggleSwitchElement.tsx | 45 ++++++++ .../src/hooks/useUiKitState.ts | 33 +++++- .../FuselageContextualBarRenderer.tsx | 1 + .../surfaces/FuselageModalSurfaceRenderer.tsx | 14 ++- .../src/surfaces/FuselageSurfaceRenderer.tsx | 105 ++++++++++++++++++ .../Display/RenderPayload/RenderPayload.tsx | 2 + .../src/Payload/BlocksTree.ts | 30 +++++ .../src/Payload/action/checkbox.ts | 40 +++++++ .../src/Payload/action/index.ts | 8 ++ .../src/Payload/action/radioButton.ts | 38 +++++++ .../src/Payload/action/timePicker.ts | 21 ++++ .../src/Payload/action/toggleSwitch.ts | 40 +++++++ .../src/Payload/callout/index.ts | 16 +++ yarn.lock | 6 +- 20 files changed, 546 insertions(+), 9 deletions(-) create mode 100644 .changeset/loud-sheep-try.md create mode 100644 packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx create mode 100644 packages/fuselage-ui-kit/src/elements/CheckboxElement.tsx create mode 100644 packages/fuselage-ui-kit/src/elements/RadioButtonElement.tsx create mode 100644 packages/fuselage-ui-kit/src/elements/TimePickerElement.tsx create mode 100644 packages/fuselage-ui-kit/src/elements/ToggleSwitchElement.tsx create mode 100644 packages/uikit-playground/src/Payload/action/checkbox.ts create mode 100644 packages/uikit-playground/src/Payload/action/radioButton.ts create mode 100644 packages/uikit-playground/src/Payload/action/timePicker.ts create mode 100644 packages/uikit-playground/src/Payload/action/toggleSwitch.ts create mode 100644 packages/uikit-playground/src/Payload/callout/index.ts diff --git a/.changeset/loud-sheep-try.md b/.changeset/loud-sheep-try.md new file mode 100644 index 000000000000..f82d0d069554 --- /dev/null +++ b/.changeset/loud-sheep-try.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/fuselage-ui-kit": minor +"@rocket.chat/uikit-playground": minor +--- + +feat: Adding new UIKit components: Callout, Checkbox, Radio Button, Time Picker, Toast Bar, Toggle Switch, Tab Navigation diff --git a/packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx b/packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx new file mode 100644 index 000000000000..89ffd0d9269a --- /dev/null +++ b/packages/fuselage-ui-kit/src/blocks/CalloutBlock.tsx @@ -0,0 +1,20 @@ +import { Callout } from '@rocket.chat/fuselage'; +import * as UiKit from '@rocket.chat/ui-kit'; +import type { ReactElement } from 'react'; + +import type { BlockProps } from '../utils/BlockProps'; + +type CalloutBlockProps = BlockProps<UiKit.CalloutBlock>; + +const CalloutBlock = ({ + block, + surfaceRenderer, +}: CalloutBlockProps): ReactElement => { + return ( + <Callout type={block.variant} icon={block.icon} title={block.title?.text}> + {surfaceRenderer.renderTextObject(block.text, 0, UiKit.BlockContext.NONE)} + </Callout> + ); +}; + +export default CalloutBlock; diff --git a/packages/fuselage-ui-kit/src/contexts/kitContext.ts b/packages/fuselage-ui-kit/src/contexts/kitContext.ts index f4a0687e5af0..181a1158f168 100644 --- a/packages/fuselage-ui-kit/src/contexts/kitContext.ts +++ b/packages/fuselage-ui-kit/src/contexts/kitContext.ts @@ -38,7 +38,9 @@ export const kitContext = createContext<UiKitContext>(defaultContext); export const useUiKitContext = () => useContext(kitContext); -export const useUiKitStateValue = <T extends string | number | undefined>( +export const useUiKitStateValue = < + T extends string | string[] | number | undefined +>( actionId: string, initialValue: T ): { diff --git a/packages/fuselage-ui-kit/src/elements/CheckboxElement.tsx b/packages/fuselage-ui-kit/src/elements/CheckboxElement.tsx new file mode 100644 index 000000000000..9861b8c6214b --- /dev/null +++ b/packages/fuselage-ui-kit/src/elements/CheckboxElement.tsx @@ -0,0 +1,45 @@ +import { CheckBox, Box } from '@rocket.chat/fuselage'; +import * as UiKit from '@rocket.chat/ui-kit'; +import type { ReactElement } from 'react'; + +import { useUiKitState } from '../hooks/useUiKitState'; +import type { BlockProps } from '../utils/BlockProps'; + +type CheckboxElementProps = BlockProps<UiKit.CheckboxElement>; + +const CheckboxElement = ({ + block, + context, + surfaceRenderer, +}: CheckboxElementProps): ReactElement => { + const [{ loading, value }, action] = useUiKitState(block, context); + const { options } = block; + + return ( + <Box> + {options.map((option: UiKit.Option) => { + const isChecked = value?.includes(option.value); + + return ( + <Box key={option.value} pb='x4'> + <CheckBox + disabled={loading} + value={option.value} + checked={isChecked} + onChange={action} + /> + <Box is='label' pis='x8'> + {surfaceRenderer.renderTextObject( + option.text, + 0, + UiKit.BlockContext.NONE + )} + </Box> + </Box> + ); + })} + </Box> + ); +}; + +export default CheckboxElement; diff --git a/packages/fuselage-ui-kit/src/elements/RadioButtonElement.tsx b/packages/fuselage-ui-kit/src/elements/RadioButtonElement.tsx new file mode 100644 index 000000000000..7d76fcb428ef --- /dev/null +++ b/packages/fuselage-ui-kit/src/elements/RadioButtonElement.tsx @@ -0,0 +1,43 @@ +import { Box, RadioButton } from '@rocket.chat/fuselage'; +import * as UiKit from '@rocket.chat/ui-kit'; +import type { ReactElement } from 'react'; + +import { useUiKitState } from '../hooks/useUiKitState'; +import type { BlockProps } from '../utils/BlockProps'; + +type RadioButtonElementProps = BlockProps<UiKit.RadioButtonElement>; + +const RadioButtonElement = ({ + block, + context, + surfaceRenderer, +}: RadioButtonElementProps): ReactElement => { + const [{ loading, value }, action] = useUiKitState(block, context); + const { options } = block; + + return ( + <Box> + {options.map((option: UiKit.Option) => { + return ( + <Box key={option.value} pb='x4'> + <RadioButton + disabled={loading} + checked={value === option.value} + value={option.value} + onChange={action} + /> + <Box is='label' pis='x8'> + {surfaceRenderer.renderTextObject( + option.text, + 0, + UiKit.BlockContext.NONE + )} + </Box> + </Box> + ); + })} + </Box> + ); +}; + +export default RadioButtonElement; diff --git a/packages/fuselage-ui-kit/src/elements/TimePickerElement.tsx b/packages/fuselage-ui-kit/src/elements/TimePickerElement.tsx new file mode 100644 index 000000000000..9814b412e6e3 --- /dev/null +++ b/packages/fuselage-ui-kit/src/elements/TimePickerElement.tsx @@ -0,0 +1,38 @@ +import { InputBox } from '@rocket.chat/fuselage'; +import type * as UiKit from '@rocket.chat/ui-kit'; +import type { ReactElement } from 'react'; + +import { useUiKitState } from '../hooks/useUiKitState'; +import type { BlockProps } from '../utils/BlockProps'; +import { fromTextObjectToString } from '../utils/fromTextObjectToString'; + +type TimePickerElementProps = BlockProps<UiKit.TimePickerElement>; + +const TimePickerElement = ({ + block, + context, + surfaceRenderer, +}: TimePickerElementProps): ReactElement => { + const [{ loading, value, error }, action] = useUiKitState(block, context); + const { actionId, placeholder } = block; + + return ( + <InputBox + type='time' + error={error} + value={value} + disabled={loading} + id={actionId} + name={actionId} + rows={6} + placeholder={ + placeholder + ? fromTextObjectToString(surfaceRenderer, placeholder, 0) + : undefined + } + onInput={action} + /> + ); +}; + +export default TimePickerElement; diff --git a/packages/fuselage-ui-kit/src/elements/ToggleSwitchElement.tsx b/packages/fuselage-ui-kit/src/elements/ToggleSwitchElement.tsx new file mode 100644 index 000000000000..647f806e428c --- /dev/null +++ b/packages/fuselage-ui-kit/src/elements/ToggleSwitchElement.tsx @@ -0,0 +1,45 @@ +import { Box, ToggleSwitch } from '@rocket.chat/fuselage'; +import * as UiKit from '@rocket.chat/ui-kit'; +import { type ReactElement } from 'react'; + +import { useUiKitState } from '../hooks/useUiKitState'; +import type { BlockProps } from '../utils/BlockProps'; + +type ToggleSwitchElementProps = BlockProps<UiKit.ToggleSwitchElement>; + +const ToggleSwitchElement = ({ + block, + context, + surfaceRenderer, +}: ToggleSwitchElementProps): ReactElement => { + const [{ value, loading }, action] = useUiKitState(block, context); + const { options } = block; + + return ( + <Box> + {options.map((option: UiKit.Option) => { + const isChecked = value.includes(option.value); + + return ( + <Box key={option.value} pb='x4'> + <ToggleSwitch + disabled={loading} + value={option.value} + checked={isChecked} + onChange={action} + /> + <Box is='label' pis='x8'> + {surfaceRenderer.renderTextObject( + option.text, + 0, + UiKit.BlockContext.NONE + )} + </Box> + </Box> + ); + })} + </Box> + ); +}; + +export default ToggleSwitchElement; diff --git a/packages/fuselage-ui-kit/src/hooks/useUiKitState.ts b/packages/fuselage-ui-kit/src/hooks/useUiKitState.ts index 5c05a1e448bb..835e3f28d6a5 100644 --- a/packages/fuselage-ui-kit/src/hooks/useUiKitState.ts +++ b/packages/fuselage-ui-kit/src/hooks/useUiKitState.ts @@ -18,11 +18,24 @@ const hasInitialValue = <TElement extends UiKit.ActionableElement>( ): element is TElement & { initialValue: number | string } => 'initialValue' in element; +const hasInitialTime = <TElement extends UiKit.ActionableElement>( + element: TElement +): element is TElement & { initialTime: string } => 'initialTime' in element; + +const hasInitialDate = <TElement extends UiKit.ActionableElement>( + element: TElement +): element is TElement & { initialDate: string } => 'initialDate' in element; + const hasInitialOption = <TElement extends UiKit.ActionableElement>( element: TElement ): element is TElement & { initialOption: UiKit.Option } => 'initialOption' in element; +const hasInitialOptions = <TElement extends UiKit.ActionableElement>( + element: TElement +): element is TElement & { initialOptions: UiKit.Option[] } => + 'initialOptions' in element; + export const useUiKitState: <TElement extends UiKit.ActionableElement>( element: TElement, context: UiKit.BlockContext @@ -45,7 +58,11 @@ export const useUiKitState: <TElement extends UiKit.ActionableElement>( const initialValue = (hasInitialValue(rest) && rest.initialValue) || + (hasInitialTime(rest) && rest.initialTime) || + (hasInitialDate(rest) && rest.initialDate) || (hasInitialOption(rest) && rest.initialOption.value) || + (hasInitialOptions(rest) && + rest.initialOptions.map((option) => option.value)) || undefined; const { value: _value, error } = useUiKitStateValue(actionId, initialValue); @@ -54,10 +71,22 @@ export const useUiKitState: <TElement extends UiKit.ActionableElement>( const actionFunction = useMutableCallback(async (e) => { const { - target: { value }, + target: { value: elValue }, } = e; setLoading(true); - setValue(value); + + if (Array.isArray(value)) { + const idx = value.findIndex((value) => value === elValue); + + if (idx > -1) { + setValue(value.filter((_, i) => i !== idx)); + } else { + setValue([...value, elValue]); + } + } else { + setValue(elValue); + } + state && (await state({ blockId, appId, actionId, value, viewId }, e)); await action( { diff --git a/packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx b/packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx index ebe6453ba360..32a2d13c1675 100644 --- a/packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx +++ b/packages/fuselage-ui-kit/src/surfaces/FuselageContextualBarRenderer.tsx @@ -10,6 +10,7 @@ export class FuselageContextualBarSurfaceRenderer extends FuselageSurfaceRendere 'input', 'section', 'preview', + 'callout', ]); } } diff --git a/packages/fuselage-ui-kit/src/surfaces/FuselageModalSurfaceRenderer.tsx b/packages/fuselage-ui-kit/src/surfaces/FuselageModalSurfaceRenderer.tsx index cdc14de4cd8b..95ca84cdab23 100644 --- a/packages/fuselage-ui-kit/src/surfaces/FuselageModalSurfaceRenderer.tsx +++ b/packages/fuselage-ui-kit/src/surfaces/FuselageModalSurfaceRenderer.tsx @@ -1,8 +1,16 @@ -import type { FuselageSurfaceRendererProps } from './FuselageSurfaceRenderer'; import { FuselageSurfaceRenderer } from './FuselageSurfaceRenderer'; export class FuselageModalSurfaceRenderer extends FuselageSurfaceRenderer { - public constructor(allowedBlocks?: FuselageSurfaceRendererProps) { - super(allowedBlocks); + public constructor() { + super([ + 'actions', + 'context', + 'divider', + 'image', + 'input', + 'section', + 'preview', + 'callout', + ]); } } diff --git a/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx b/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx index c808878a9fb1..ae4365c2dddb 100644 --- a/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx +++ b/packages/fuselage-ui-kit/src/surfaces/FuselageSurfaceRenderer.tsx @@ -16,6 +16,11 @@ import MultiStaticSelectElement from '../elements/MultiStaticSelectElement'; import OverflowElement from '../elements/OverflowElement'; import PlainTextInputElement from '../elements/PlainTextInputElement'; import StaticSelectElement from '../elements/StaticSelectElement'; +import ToggleSwitchElement from '../elements/ToggleSwitchElement'; +import RadioButtonElement from '../elements/RadioButtonElement'; +import CheckboxElement from '../elements/CheckboxElement'; +import CalloutBlock from '../blocks/CalloutBlock'; +import TimePickerElement from '../elements/TimePickerElement'; import MarkdownTextElement from '../elements/MarkdownTextElement'; import PlainTextElement from '../elements/PlainTextElement'; @@ -360,4 +365,104 @@ export class FuselageSurfaceRenderer extends UiKit.SurfaceRenderer<ReactElement> /> ); } + + toggle_switch( + block: UiKit.ToggleSwitchElement, + context: UiKit.BlockContext, + index: number + ): ReactElement | null { + if (context === UiKit.BlockContext.BLOCK) { + return null; + } + + return ( + <ToggleSwitchElement + key={block.actionId || index} + block={block} + context={context} + index={index} + surfaceRenderer={this} + /> + ); + } + + radio_button( + block: UiKit.RadioButtonElement, + context: UiKit.BlockContext, + index: number + ): ReactElement | null { + if (context === UiKit.BlockContext.BLOCK) { + return null; + } + + return ( + <RadioButtonElement + key={block.actionId || index} + block={block} + context={context} + index={index} + surfaceRenderer={this} + /> + ); + } + + checkbox( + block: UiKit.CheckboxElement, + context: UiKit.BlockContext, + index: number + ): ReactElement | null { + if (context === UiKit.BlockContext.BLOCK) { + return null; + } + + return ( + <CheckboxElement + key={block.actionId || index} + block={block} + context={context} + index={index} + surfaceRenderer={this} + /> + ); + } + + callout( + block: UiKit.CalloutBlock, + context: UiKit.BlockContext, + index: number + ): ReactElement | null { + if (context === UiKit.BlockContext.BLOCK) { + return ( + <CalloutBlock + key={index} + block={block} + context={context} + index={index} + surfaceRenderer={this} + /> + ); + } + + return null; + } + + time_picker( + block: UiKit.TimePickerElement, + context: UiKit.BlockContext, + index: number + ): ReactElement | null { + if (context === UiKit.BlockContext.BLOCK) { + return null; + } + + return ( + <TimePickerElement + key={block.actionId || index} + block={block} + context={context} + index={index} + surfaceRenderer={this} + /> + ); + } } diff --git a/packages/uikit-playground/src/Components/Preview/Display/RenderPayload/RenderPayload.tsx b/packages/uikit-playground/src/Components/Preview/Display/RenderPayload/RenderPayload.tsx index f75134e49105..9cb989319b81 100644 --- a/packages/uikit-playground/src/Components/Preview/Display/RenderPayload/RenderPayload.tsx +++ b/packages/uikit-playground/src/Components/Preview/Display/RenderPayload/RenderPayload.tsx @@ -3,6 +3,7 @@ import { UiKitModal as uiKitModal, UiKitBanner as uiKitBanner, UiKitMessage as uiKitMessage, + UiKitContextualBar as uiKitContextualBar, } from '@rocket.chat/fuselage-ui-kit'; import type { LayoutBlock } from '@rocket.chat/ui-kit'; @@ -22,6 +23,7 @@ const RenderPayload = ({ '1': () => uiKitMessage(payload), '2': () => uiKitBanner(payload), '3': () => uiKitModal(payload), + '4': () => uiKitContextualBar(payload) }; return ( diff --git a/packages/uikit-playground/src/Payload/BlocksTree.ts b/packages/uikit-playground/src/Payload/BlocksTree.ts index 1b561d5448d8..30c3ad16bab5 100644 --- a/packages/uikit-playground/src/Payload/BlocksTree.ts +++ b/packages/uikit-playground/src/Payload/BlocksTree.ts @@ -16,6 +16,10 @@ import { actionWithMultiStaticSelect, actionWithDatePicker, actionWithLinearScale, + actionWithToggleSwitch, + actionWithRadioButton, + actionWithCheckbox, + actionWithTimePicker, } from './action'; import { contextWithPlainText, @@ -24,6 +28,7 @@ import { contextWithAllElements, } from './context'; import { divider } from './divider'; +import { callout } from './callout'; import { imageWithTitle, imageWithoutTitle } from './image'; import { inputWithSingleLineInput, @@ -118,10 +123,26 @@ const BlocksTree: Item = [ label: 'date Picker', payload: actionWithDatePicker, }, + { + label: 'time Picker', + payload: actionWithTimePicker, + }, { label: 'linear scale', payload: actionWithLinearScale, }, + { + label: 'toggle switch', + payload: actionWithToggleSwitch, + }, + { + label: 'radio buttons', + payload: actionWithRadioButton, + }, + { + label: 'checkbox', + payload: actionWithCheckbox, + }, ], }, { @@ -298,6 +319,15 @@ const BlocksTree: Item = [ }, ], }, + { + label: 'callout', + branches: [ + { + label: 'Plain', + payload: callout, + }, + ], + }, ]; export default BlocksTree; diff --git a/packages/uikit-playground/src/Payload/action/checkbox.ts b/packages/uikit-playground/src/Payload/action/checkbox.ts new file mode 100644 index 000000000000..297bcd964d47 --- /dev/null +++ b/packages/uikit-playground/src/Payload/action/checkbox.ts @@ -0,0 +1,40 @@ +import type { LayoutBlock } from '@rocket.chat/ui-kit'; + +export const actionWithCheckbox: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'checkbox', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + options: [ + { + text: { + type: 'plain_text', + text: 'Option 1', + }, + value: 'value-1', + }, + { + text: { + type: 'plain_text', + text: 'Option initial', + }, + value: 'value-2', + }, + ], + initialOptions: [ + { + text: { + type: 'plain_text', + text: 'Option initial', + }, + value: 'value-2', + }, + ], + }, + ], + }, +]; diff --git a/packages/uikit-playground/src/Payload/action/index.ts b/packages/uikit-playground/src/Payload/action/index.ts index ca9506b515f7..2d2f75a9c58e 100644 --- a/packages/uikit-playground/src/Payload/action/index.ts +++ b/packages/uikit-playground/src/Payload/action/index.ts @@ -11,3 +11,11 @@ export * from './menu'; export * from './datePicker'; export * from './linearScale'; + +export * from './toggleSwitch'; + +export * from './radioButton'; + +export * from './checkbox'; + +export * from './timePicker'; diff --git a/packages/uikit-playground/src/Payload/action/radioButton.ts b/packages/uikit-playground/src/Payload/action/radioButton.ts new file mode 100644 index 000000000000..6f4845930032 --- /dev/null +++ b/packages/uikit-playground/src/Payload/action/radioButton.ts @@ -0,0 +1,38 @@ +import type { LayoutBlock } from '@rocket.chat/ui-kit'; + +export const actionWithRadioButton: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'radio_button', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + options: [ + { + text: { + type: 'plain_text', + text: 'Option 1', + }, + value: 'value-1', + }, + { + text: { + type: 'plain_text', + text: 'Option initial', + }, + value: 'value-2', + }, + ], + initialOption: { + text: { + type: 'plain_text', + text: 'Option initial', + }, + value: 'value-2', + }, + }, + ], + }, +]; diff --git a/packages/uikit-playground/src/Payload/action/timePicker.ts b/packages/uikit-playground/src/Payload/action/timePicker.ts new file mode 100644 index 000000000000..3482f74ed9ca --- /dev/null +++ b/packages/uikit-playground/src/Payload/action/timePicker.ts @@ -0,0 +1,21 @@ +import type { LayoutBlock } from '@rocket.chat/ui-kit'; + +export const actionWithTimePicker: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'time_picker', + initialTime: '10:30', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + placeholder: { + type: 'plain_text', + text: 'Select a time', + emoji: true, + }, + }, + ], + }, +]; diff --git a/packages/uikit-playground/src/Payload/action/toggleSwitch.ts b/packages/uikit-playground/src/Payload/action/toggleSwitch.ts new file mode 100644 index 000000000000..b19c4fce1237 --- /dev/null +++ b/packages/uikit-playground/src/Payload/action/toggleSwitch.ts @@ -0,0 +1,40 @@ +import type { LayoutBlock } from '@rocket.chat/ui-kit'; + +export const actionWithToggleSwitch: readonly LayoutBlock[] = [ + { + type: 'actions', + elements: [ + { + type: 'toggle_switch', + appId: 'app-id', + blockId: 'block-id', + actionId: 'action-id', + options: [ + { + text: { + type: 'plain_text', + text: 'Toggle 1', + }, + value: 'value-1', + }, + { + text: { + type: 'plain_text', + text: 'Toggle initial option', + }, + value: 'value-2', + }, + ], + initialOptions: [ + { + text: { + type: 'plain_text', + text: 'Toggle initial option', + }, + value: 'value-2', + }, + ], + }, + ], + }, +]; diff --git a/packages/uikit-playground/src/Payload/callout/index.ts b/packages/uikit-playground/src/Payload/callout/index.ts new file mode 100644 index 000000000000..70509d6e61e5 --- /dev/null +++ b/packages/uikit-playground/src/Payload/callout/index.ts @@ -0,0 +1,16 @@ +import type { LayoutBlock } from '@rocket.chat/ui-kit'; + +export const callout: readonly LayoutBlock[] = [ + { + type: 'callout', + title: { + type: 'plain_text', + text: 'Callout Title', + }, + text: { + type: 'plain_text', + text: 'Callout Text', + }, + icon: 'rocket', + }, +]; diff --git a/yarn.lock b/yarn.lock index 4325b0eaa0b8..c5640123702e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8806,11 +8806,11 @@ __metadata: linkType: soft "@rocket.chat/ui-kit@npm:next": - version: 0.32.0-dev.321 - resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.321" + version: 0.32.0-dev.330 + resolution: "@rocket.chat/ui-kit@npm:0.32.0-dev.330" peerDependencies: "@rocket.chat/icons": "*" - checksum: 9e237ac9c3553455d2167ef945070fcdfa90c3403599cc0e6a0c0de6d5663851ce29f4cfc9e0b6809a711620431a2a9f69398041ddffcbc41fe99a90aa1ae483 + checksum: f43cadeccf12770e9a8f67976deda4d2ebe5ee7af845ef78fb4c5a4bbf046ba15008e8a73a2de56bb5b16138a4722b6f0d02a003d6c6a0219c561c906b58eeb7 languageName: node linkType: hard From 69bdda1a0b2b4891e03be45b4521328683b7f1f8 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Sat, 22 Jul 2023 09:20:46 -0300 Subject: [PATCH 147/149] chore: remove barrel imports server (#29890) --- .../app/api/server/helpers/getUserInfo.ts | 3 +- .../app/api/server/lib/getServerInfo.ts | 2 +- apps/meteor/app/api/server/v1/commands.ts | 2 +- apps/meteor/app/api/server/v1/users.ts | 8 +++-- .../app/apps/server/bridges/commands.ts | 2 +- .../app/apps/server/bridges/messages.ts | 2 +- apps/meteor/app/apps/server/bridges/users.ts | 4 ++- .../server/lib/restrictLoginAttempts.ts | 2 +- apps/meteor/app/cas/server/cas_server.js | 2 +- .../server/functions/saveRoomName.ts | 2 +- apps/meteor/app/crowd/server/crowd.ts | 4 +-- .../hooks/propagateDiscussionMetadata.ts | 2 +- .../server/methods/createDiscussion.ts | 5 +++- apps/meteor/app/emoji-emojione/server/lib.ts | 2 +- .../server/lib/RocketChat.ErrorHandler.ts | 2 +- .../federation/server/endpoints/dispatch.js | 2 +- .../server/hooks/afterCreateDirectRoom.js | 2 +- .../server/hooks/afterCreateRoom.js | 2 +- .../app/file-upload/server/lib/proxy.ts | 2 +- .../server/classes/ImportDataConverter.ts | 5 +++- .../irc-bridge/peerHandlers/joinedChannel.js | 3 +- .../irc-bridge/peerHandlers/leftChannel.js | 2 +- .../irc-bridge/peerHandlers/sentMessage.js | 2 +- .../lib/server/functions/createDirectRoom.ts | 2 +- apps/meteor/app/lib/server/functions/index.ts | 30 ------------------- .../server/functions/notifications/email.js | 2 +- .../lib/server/functions/saveCustomFields.ts | 3 +- .../app/lib/server/functions/saveUser.js | 4 ++- .../app/lib/server/functions/setEmail.ts | 2 +- .../app/lib/server/functions/setUsername.ts | 3 +- apps/meteor/app/lib/server/index.ts | 1 - apps/meteor/app/lib/server/lib/bugsnag.ts | 2 +- .../app/lib/server/methods/addUsersToRoom.ts | 2 +- .../app/lib/server/methods/createChannel.ts | 2 +- .../lib/server/methods/createPrivateGroup.ts | 2 +- .../server/methods/deleteUserOwnAccount.ts | 2 +- .../methods/executeSlashCommandPreview.ts | 2 +- .../server/methods/getSlashCommandPreviews.ts | 2 +- .../server/methods/getUsernameSuggestion.ts | 2 +- .../lib/server/methods/insertOrUpdateUser.ts | 2 +- .../lib/server/methods/joinDefaultChannels.ts | 2 +- .../meteor/app/lib/server/methods/joinRoom.ts | 2 +- .../app/lib/server/methods/leaveRoom.ts | 2 +- .../app/lib/server/methods/sendMessage.ts | 2 +- .../meteor/app/lib/server/methods/setEmail.ts | 2 +- .../app/lib/server/methods/setRealName.ts | 2 +- .../app/lib/server/methods/unarchiveRoom.ts | 2 +- .../app/lib/server/methods/updateMessage.ts | 2 +- .../livechat/imports/server/rest/upload.ts | 2 +- .../meteor/app/livechat/server/api/v1/room.ts | 2 +- .../server/hooks/offlineMessageToChannel.ts | 2 +- .../app/livechat/server/lib/Analytics.js | 2 +- .../server/lib/analytics/dashboards.ts | 2 +- .../server/functions/sendMail.ts | 2 +- .../app/message-pin/server/pinMessage.ts | 2 +- .../app/message-star/server/starMessage.ts | 2 +- .../meteor-accounts-saml/server/lib/SAML.ts | 5 +++- .../app/metrics/server/lib/collectMetrics.ts | 2 +- apps/meteor/app/oembed/server/server.ts | 2 +- .../methods/saveNotificationSettings.ts | 2 +- .../app/reactions/server/setReaction.ts | 2 +- .../client/slackbridge_import.client.js | 2 +- .../app/slackbridge/server/RocketAdapter.js | 4 ++- .../app/slackbridge/server/SlackAdapter.js | 7 ++++- .../server/slackbridge_import.server.js | 2 +- .../app/slashcommands-hide/server/hide.ts | 2 +- .../server/server.ts | 2 +- .../server/lib/getAppsStatistics.js | 2 +- .../app/statistics/server/lib/statistics.ts | 3 +- apps/meteor/app/ui-master/server/inject.ts | 2 +- .../popup/hooks/useComposerBoxPopupQueries.ts | 2 +- .../server/methods/getUserStatusText.ts | 2 +- apps/meteor/app/utils/server/index.ts | 13 -------- .../server/functions/checkVersionUpdate.ts | 2 +- .../server/functions/getNewUpdates.ts | 2 +- .../client/providers/ServerProvider.tsx | 2 +- .../views/root/OutermostErrorBoundary.tsx | 2 +- .../client/apps/communication/websockets.js | 2 +- .../ee/server/apps/communication/rest.ts | 2 +- .../ee/server/apps/marketplace/appInstall.ts | 2 +- .../ee/server/lib/deviceManagement/session.ts | 2 +- apps/meteor/ee/server/lib/ldap/Manager.ts | 6 ++-- apps/meteor/ee/server/lib/oauth/Manager.ts | 5 ++-- .../rocket-chat/slash-commands/index.ts | 2 +- .../EmailInbox/EmailInbox_Outgoing.ts | 2 +- apps/meteor/server/importPackages.ts | 1 - apps/meteor/server/lib/ldap/Manager.ts | 2 +- apps/meteor/server/lib/migrations.ts | 2 +- apps/meteor/server/methods/channelsList.ts | 2 +- apps/meteor/server/methods/deleteUser.ts | 2 +- apps/meteor/server/methods/saveUserProfile.ts | 3 +- apps/meteor/server/routes/avatar/utils.js | 2 +- .../rocket-chat/adapters/Message.ts | 6 ++-- .../rocket-chat/adapters/Room.ts | 6 ++-- .../rocket-chat/adapters/User.ts | 3 +- .../rocket-chat/slash-commands/index.ts | 2 +- apps/meteor/server/startup/initialData.js | 2 +- apps/meteor/server/startup/serverRunning.js | 3 +- 98 files changed, 139 insertions(+), 145 deletions(-) delete mode 100644 apps/meteor/app/lib/server/functions/index.ts delete mode 100644 apps/meteor/app/utils/server/index.ts diff --git a/apps/meteor/app/api/server/helpers/getUserInfo.ts b/apps/meteor/app/api/server/helpers/getUserInfo.ts index 2538c59175fd..2357b34a9348 100644 --- a/apps/meteor/app/api/server/helpers/getUserInfo.ts +++ b/apps/meteor/app/api/server/helpers/getUserInfo.ts @@ -1,7 +1,8 @@ import type { IUser, IUserEmail } from '@rocket.chat/core-typings'; import { settings } from '../../../settings/server'; -import { getUserPreference, getURL } from '../../../utils/server'; +import { getUserPreference } from '../../../utils/server/lib/getUserPreference'; +import { getURL } from '../../../utils/server/getURL'; const isVerifiedEmail = (me: IUser): false | IUserEmail | undefined => { if (!me || !Array.isArray(me.emails)) { diff --git a/apps/meteor/app/api/server/lib/getServerInfo.ts b/apps/meteor/app/api/server/lib/getServerInfo.ts index a4f9f953b8bd..de8c64f2b257 100644 --- a/apps/meteor/app/api/server/lib/getServerInfo.ts +++ b/apps/meteor/app/api/server/lib/getServerInfo.ts @@ -1,4 +1,4 @@ -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; type ServerInfo = diff --git a/apps/meteor/app/api/server/v1/commands.ts b/apps/meteor/app/api/server/v1/commands.ts index 8e3e7ceb3edd..44b38beb4f09 100644 --- a/apps/meteor/app/api/server/v1/commands.ts +++ b/apps/meteor/app/api/server/v1/commands.ts @@ -3,7 +3,7 @@ import { Random } from '@rocket.chat/random'; import objectPath from 'object-path'; import { Messages } from '@rocket.chat/models'; -import { slashCommands } from '../../../utils/server'; +import { slashCommands } from '../../../utils/server/slashCommand'; import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; import { API } from '../api'; import { getLoggedInUser } from '../helpers/getLoggedInUser'; diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index 56c15c6e35c8..8d00dbb3009c 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -24,7 +24,11 @@ import { Team, api } from '@rocket.chat/core-services'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { settings } from '../../../settings/server'; -import { validateCustomFields, saveUser, saveCustomFieldsWithoutValidation, setUserAvatar, saveCustomFields } from '../../../lib/server'; +import { saveCustomFields } from '../../../lib/server/functions/saveCustomFields'; +import { saveCustomFieldsWithoutValidation } from '../../../lib/server/functions/saveCustomFieldsWithoutValidation'; +import { saveUser } from '../../../lib/server/functions/saveUser'; +import { setUserAvatar } from '../../../lib/server/functions/setUserAvatar'; +import { validateCustomFields } from '../../../lib/server/functions/validateCustomFields'; import { checkUsernameAvailability, checkUsernameAvailabilityWithValidation, @@ -37,7 +41,7 @@ import { getUserForCheck, emailCheck } from '../../../2fa/server/code'; import { resetUserE2EEncriptionKey } from '../../../../server/lib/resetUserE2EKey'; import { resetTOTP } from '../../../2fa/server/functions/resetTOTP'; import { isValidQuery } from '../lib/isValidQuery'; -import { getURL } from '../../../utils/server'; +import { getURL } from '../../../utils/server/getURL'; import { getUploadFormData } from '../lib/getUploadFormData'; import { getPaginationItems } from '../helpers/getPaginationItems'; import { getUserFromParams } from '../helpers/getUserFromParams'; diff --git a/apps/meteor/app/apps/server/bridges/commands.ts b/apps/meteor/app/apps/server/bridges/commands.ts index 35c2bb253b32..050027ecd600 100644 --- a/apps/meteor/app/apps/server/bridges/commands.ts +++ b/apps/meteor/app/apps/server/bridges/commands.ts @@ -4,7 +4,7 @@ import { SlashCommandContext } from '@rocket.chat/apps-engine/definition/slashco import { CommandBridge } from '@rocket.chat/apps-engine/server/bridges/CommandBridge'; import type { IMessage, RequiredField, SlashCommand, SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { slashCommands } from '../../../utils/server'; +import { slashCommands } from '../../../utils/server/slashCommand'; import { Utilities } from '../../../../ee/lib/misc/Utilities'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { parseParameters } from '../../../../lib/utils/parseParameters'; diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index f6a50a9e6ab8..64e1f4dd44dc 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -10,7 +10,7 @@ import { updateMessage } from '../../../lib/server/functions/updateMessage'; import { executeSendMessage } from '../../../lib/server/methods/sendMessage'; import notifications from '../../../notifications/server/lib/Notifications'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; -import { deleteMessage } from '../../../lib/server'; +import { deleteMessage } from '../../../lib/server/functions/deleteMessage'; export class AppMessageBridge extends MessageBridge { // eslint-disable-next-line no-empty-function diff --git a/apps/meteor/app/apps/server/bridges/users.ts b/apps/meteor/app/apps/server/bridges/users.ts index 27bac79b1030..bd336f414191 100644 --- a/apps/meteor/app/apps/server/bridges/users.ts +++ b/apps/meteor/app/apps/server/bridges/users.ts @@ -5,7 +5,9 @@ import { Subscriptions, Users } from '@rocket.chat/models'; import { Presence } from '@rocket.chat/core-services'; import type { UserStatus } from '@rocket.chat/core-typings'; -import { setUserAvatar, deleteUser, getUserCreatedByApp } from '../../../lib/server/functions'; +import { deleteUser } from '../../../lib/server/functions/deleteUser'; +import { getUserCreatedByApp } from '../../../lib/server/functions/getUserCreatedByApp'; +import { setUserAvatar } from '../../../lib/server/functions/setUserAvatar'; import { checkUsernameAvailability } from '../../../lib/server/functions/checkUsernameAvailability'; import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator'; import { setUserActiveStatus } from '../../../lib/server/functions/setUserActiveStatus'; diff --git a/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts b/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts index 743521df6721..716d03ce6097 100644 --- a/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts +++ b/apps/meteor/app/authentication/server/lib/restrictLoginAttempts.ts @@ -5,7 +5,7 @@ import moment from 'moment'; import { addMinutesToADate } from '../../../../lib/utils/addMinutesToADate'; import { getClientAddress } from '../../../../server/lib/getClientAddress'; -import { sendMessage } from '../../../lib/server/functions'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { Logger } from '../../../logger/server'; import { settings } from '../../../settings/server'; import type { ILoginAttempt } from '../ILoginAttempt'; diff --git a/apps/meteor/app/cas/server/cas_server.js b/apps/meteor/app/cas/server/cas_server.js index 230de6027cb4..1abb08663eba 100644 --- a/apps/meteor/app/cas/server/cas_server.js +++ b/apps/meteor/app/cas/server/cas_server.js @@ -10,7 +10,7 @@ import { validate } from '@rocket.chat/cas-validate'; import { logger } from './cas_rocketchat'; import { settings } from '../../settings/server'; -import { _setRealName } from '../../lib/server'; +import { _setRealName } from '../../lib/server/functions/setRealName'; import { createRoom } from '../../lib/server/functions/createRoom'; RoutePolicy.declare('/_cas/', 'network'); diff --git a/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts b/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts index 864475a0d229..1458fcabe2ae 100644 --- a/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts +++ b/apps/meteor/app/channel-settings/server/functions/saveRoomName.ts @@ -5,7 +5,7 @@ import { isRoomFederated } from '@rocket.chat/core-typings'; import { Message } from '@rocket.chat/core-services'; import type { Document, UpdateResult } from 'mongodb'; -import { getValidRoomName } from '../../../utils/server'; +import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; import { callbacks } from '../../../../lib/callbacks'; import { checkUsernameAvailability } from '../../../lib/server/functions/checkUsernameAvailability'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/crowd/server/crowd.ts b/apps/meteor/app/crowd/server/crowd.ts index b998abc631b9..38a6b57a9c88 100644 --- a/apps/meteor/app/crowd/server/crowd.ts +++ b/apps/meteor/app/crowd/server/crowd.ts @@ -5,9 +5,9 @@ import { Users } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; import { cronJobs } from '@rocket.chat/cron'; -import { _setRealName } from '../../lib/server'; +import { _setRealName } from '../../lib/server/functions/setRealName'; import { settings } from '../../settings/server'; -import { deleteUser } from '../../lib/server/functions'; +import { deleteUser } from '../../lib/server/functions/deleteUser'; import { setUserActiveStatus } from '../../lib/server/functions/setUserActiveStatus'; import { logger } from './logger'; import { crowdIntervalValuesToCronMap } from '../../../server/settings/crowd'; diff --git a/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts b/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts index 73ad09a6fcf6..1462cd798396 100644 --- a/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts +++ b/apps/meteor/app/discussion/server/hooks/propagateDiscussionMetadata.ts @@ -1,7 +1,7 @@ import { Messages, Rooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; -import { deleteRoom } from '../../../lib/server'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; /** * We need to propagate the writing of new message in a discussion to the linking diff --git a/apps/meteor/app/discussion/server/methods/createDiscussion.ts b/apps/meteor/app/discussion/server/methods/createDiscussion.ts index cd2befbf036c..b9a1b5ed67ff 100644 --- a/apps/meteor/app/discussion/server/methods/createDiscussion.ts +++ b/apps/meteor/app/discussion/server/methods/createDiscussion.ts @@ -7,7 +7,10 @@ import { Message } from '@rocket.chat/core-services'; import { hasAtLeastOnePermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; -import { createRoom, addUserToRoom, sendMessage, attachMessage } from '../../../lib/server'; +import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; +import { attachMessage } from '../../../lib/server/functions/attachMessage'; +import { createRoom } from '../../../lib/server/functions/createRoom'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; diff --git a/apps/meteor/app/emoji-emojione/server/lib.ts b/apps/meteor/app/emoji-emojione/server/lib.ts index b80b9318a828..2b064d21fb2d 100644 --- a/apps/meteor/app/emoji-emojione/server/lib.ts +++ b/apps/meteor/app/emoji-emojione/server/lib.ts @@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor'; import { emoji } from '../../emoji/server'; import { getEmojiConfig } from '../lib/getEmojiConfig'; import { isSetNotNull } from '../lib/isSetNotNull'; -import { getUserPreference } from '../../utils/server'; +import { getUserPreference } from '../../utils/server/lib/getUserPreference'; const config = getEmojiConfig(); diff --git a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts index e4a549a1f02d..91846b205652 100644 --- a/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts +++ b/apps/meteor/app/error-handler/server/lib/RocketChat.ErrorHandler.ts @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { Settings, Users, Rooms } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; -import { sendMessage } from '../../../lib/server'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { throttledCounter } from '../../../../lib/utils/throttledCounter'; const incException = throttledCounter((counter) => { diff --git a/apps/meteor/app/federation/server/endpoints/dispatch.js b/apps/meteor/app/federation/server/endpoints/dispatch.js index 1bd2fc67c096..900298e4fde4 100644 --- a/apps/meteor/app/federation/server/endpoints/dispatch.js +++ b/apps/meteor/app/federation/server/endpoints/dispatch.js @@ -7,7 +7,7 @@ import { API } from '../../../api/server'; import { serverLogger } from '../lib/logger'; import { contextDefinitions } from '../lib/context'; import { normalizers } from '../normalizers'; -import { deleteRoom } from '../../../lib/server/functions'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; import { FileUpload } from '../../../file-upload/server'; import { getFederationDomain } from '../lib/getFederationDomain'; import { decryptIfNeeded } from '../lib/crypt'; diff --git a/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js b/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js index cdad00cc439b..5436c1f5a7ce 100644 --- a/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js +++ b/apps/meteor/app/federation/server/hooks/afterCreateDirectRoom.js @@ -2,7 +2,7 @@ import { FederationRoomEvents, Subscriptions } from '@rocket.chat/models'; import { clientLogger } from '../lib/logger'; import { normalizers } from '../normalizers'; -import { deleteRoom } from '../../../lib/server/functions'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvents } from '../handler'; import { isFullyQualified } from '../functions/helpers'; diff --git a/apps/meteor/app/federation/server/hooks/afterCreateRoom.js b/apps/meteor/app/federation/server/hooks/afterCreateRoom.js index 4caa3f711de3..af180a5f95f1 100644 --- a/apps/meteor/app/federation/server/hooks/afterCreateRoom.js +++ b/apps/meteor/app/federation/server/hooks/afterCreateRoom.js @@ -2,7 +2,7 @@ import { FederationRoomEvents, Users, Subscriptions } from '@rocket.chat/models' import { clientLogger } from '../lib/logger'; import { normalizers } from '../normalizers'; -import { deleteRoom } from '../../../lib/server/functions'; +import { deleteRoom } from '../../../lib/server/functions/deleteRoom'; import { getFederationDomain } from '../lib/getFederationDomain'; import { dispatchEvents } from '../handler'; import { checkRoomType, checkRoomDomainsLength } from '../functions/helpers'; diff --git a/apps/meteor/app/file-upload/server/lib/proxy.ts b/apps/meteor/app/file-upload/server/lib/proxy.ts index 639ace6aa040..b0db5a41f2e1 100644 --- a/apps/meteor/app/file-upload/server/lib/proxy.ts +++ b/apps/meteor/app/file-upload/server/lib/proxy.ts @@ -9,7 +9,7 @@ import type createServer from 'connect'; import { UploadFS } from '../../../../server/ufs'; import { Logger } from '../../../logger/server'; -import { isDocker } from '../../../utils/server'; +import { isDocker } from '../../../utils/server/functions/isDocker'; const logger = new Logger('UploadProxy'); diff --git a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts index 0204e88c2484..7fba63ed4ca4 100644 --- a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts +++ b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts @@ -17,7 +17,10 @@ import type { import { ImportData, Rooms, Users, Subscriptions } from '@rocket.chat/models'; import type { IConversionCallbacks } from '../definitions/IConversionCallbacks'; -import { generateUsernameSuggestion, insertMessage, saveUserIdentity, addUserToDefaultChannels } from '../../../lib/server'; +import { addUserToDefaultChannels } from '../../../lib/server/functions/addUserToDefaultChannels'; +import { generateUsernameSuggestion } from '../../../lib/server/functions/getUsernameSuggestion'; +import { insertMessage } from '../../../lib/server/functions/insertMessage'; +import { saveUserIdentity } from '../../../lib/server/functions/saveUserIdentity'; import { setUserActiveStatus } from '../../../lib/server/functions/setUserActiveStatus'; import type { Logger } from '../../../../server/lib/logger/Logger'; import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js index 0433b8d463be..0968eacc5340 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/joinedChannel.js @@ -1,6 +1,7 @@ import { Users, Rooms } from '@rocket.chat/models'; -import { createRoom, addUserToRoom } from '../../../../lib/server'; +import { addUserToRoom } from '../../../../lib/server/functions/addUserToRoom'; +import { createRoom } from '../../../../lib/server/functions/createRoom'; // TODO doesn't seem to be used anywhere, remove export default async function handleJoinedChannel(args) { diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js index 8ac835c8084b..9a65f35e5037 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/leftChannel.js @@ -1,6 +1,6 @@ import { Users, Rooms } from '@rocket.chat/models'; -import { removeUserFromRoom } from '../../../../lib/server'; +import { removeUserFromRoom } from '../../../../lib/server/functions/removeUserFromRoom'; export default async function handleLeftChannel(args) { const user = await Users.findOne({ diff --git a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js index fa975cbabbe2..81c4d7c059de 100644 --- a/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js +++ b/apps/meteor/app/irc/server/irc-bridge/peerHandlers/sentMessage.js @@ -1,6 +1,6 @@ import { Users, Rooms } from '@rocket.chat/models'; -import { sendMessage, createDirectRoom } from '../../../../lib/server'; +import { sendMessage, createDirectRoom } from '../../../../lib/server/functions/sendMessage'; /* * * Get direct chat room helper diff --git a/apps/meteor/app/lib/server/functions/createDirectRoom.ts b/apps/meteor/app/lib/server/functions/createDirectRoom.ts index c9a6ce7a35c3..92db702dd4ec 100644 --- a/apps/meteor/app/lib/server/functions/createDirectRoom.ts +++ b/apps/meteor/app/lib/server/functions/createDirectRoom.ts @@ -10,7 +10,7 @@ import { Apps } from '../../../../ee/server/apps'; import { callbacks } from '../../../../lib/callbacks'; import { isTruthy } from '../../../../lib/isTruthy'; import { settings } from '../../../settings/server'; -import { getDefaultSubscriptionPref } from '../../../utils/server'; +import { getDefaultSubscriptionPref } from '../../../utils/lib/getDefaultSubscriptionPref'; const generateSubscription = ( fname: string, diff --git a/apps/meteor/app/lib/server/functions/index.ts b/apps/meteor/app/lib/server/functions/index.ts deleted file mode 100644 index 15092acbfb71..000000000000 --- a/apps/meteor/app/lib/server/functions/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -export { addUserToDefaultChannels } from './addUserToDefaultChannels'; -export { addUserToRoom } from './addUserToRoom'; -export { attachMessage } from './attachMessage'; -export { checkEmailAvailability } from './checkEmailAvailability'; -export { createRoom } from './createRoom'; -export { createDirectRoom } from './createDirectRoom'; -export { deleteMessage } from './deleteMessage'; -export { deleteRoom } from './deleteRoom'; -export { deleteUser } from './deleteUser'; -export { getUserSingleOwnedRooms } from './getUserSingleOwnedRooms'; -export { getUserCreatedByApp } from './getUserCreatedByApp'; -export { generateUsernameSuggestion } from './getUsernameSuggestion'; -export { insertMessage } from './insertMessage'; -export { isTheLastMessage } from './isTheLastMessage'; -export { removeUserFromRoom } from './removeUserFromRoom'; -export { relinquishRoomOwnerships } from './relinquishRoomOwnerships'; -export { saveCustomFields } from './saveCustomFields'; -export { saveCustomFieldsWithoutValidation } from './saveCustomFieldsWithoutValidation'; -export { saveUser } from './saveUser'; -export { saveUserIdentity } from './saveUserIdentity'; -export { sendMessage } from './sendMessage'; -export { setEmail } from './setEmail'; -export { setRealName, _setRealName } from './setRealName'; -export { getStatusText } from './getStatusText'; -export { setUserAvatar } from './setUserAvatar'; -export { _setUsername, setUsername, setUsernameWithValidation } from './setUsername'; -export { unarchiveRoom } from './unarchiveRoom'; -export { updateMessage } from './updateMessage'; -export { validateCustomFields } from './validateCustomFields'; -export { validateName } from './validateName'; diff --git a/apps/meteor/app/lib/server/functions/notifications/email.js b/apps/meteor/app/lib/server/functions/notifications/email.js index fbf7218380a7..ca530c47f117 100644 --- a/apps/meteor/app/lib/server/functions/notifications/email.js +++ b/apps/meteor/app/lib/server/functions/notifications/email.js @@ -5,7 +5,7 @@ import * as Mailer from '../../../../mailer/server/api'; import { settings } from '../../../../settings/server'; import { metrics } from '../../../../metrics/server'; import { callbacks } from '../../../../../lib/callbacks'; -import { getURL } from '../../../../utils/server'; +import { getURL } from '../../../../utils/server/getURL'; import { roomCoordinator } from '../../../../../server/lib/rooms/roomCoordinator'; import { ltrim } from '../../../../../lib/utils/stringUtils'; import { i18n } from '../../../../../server/lib/i18n'; diff --git a/apps/meteor/app/lib/server/functions/saveCustomFields.ts b/apps/meteor/app/lib/server/functions/saveCustomFields.ts index 79e8e81dbe7b..5b6e040794fa 100644 --- a/apps/meteor/app/lib/server/functions/saveCustomFields.ts +++ b/apps/meteor/app/lib/server/functions/saveCustomFields.ts @@ -1,5 +1,6 @@ import { settings } from '../../../settings/server'; -import { validateCustomFields, saveCustomFieldsWithoutValidation } from '.'; +import { saveCustomFieldsWithoutValidation } from './saveCustomFieldsWithoutValidation'; +import { validateCustomFields } from './validateCustomFields'; import { trim } from '../../../../lib/utils/stringUtils'; export const saveCustomFields = async function (userId: string, formData: Record<string, any>): Promise<void> { diff --git a/apps/meteor/app/lib/server/functions/saveUser.js b/apps/meteor/app/lib/server/functions/saveUser.js index 650a9b05bc31..94eb3246989f 100644 --- a/apps/meteor/app/lib/server/functions/saveUser.js +++ b/apps/meteor/app/lib/server/functions/saveUser.js @@ -13,7 +13,9 @@ import { passwordPolicy } from '../lib/passwordPolicy'; import { validateEmailDomain } from '../lib'; import { getNewUserRoles } from '../../../../server/services/user/lib/getNewUserRoles'; import { saveUserIdentity } from './saveUserIdentity'; -import { checkEmailAvailability, setUserAvatar, setEmail } from '.'; +import { checkEmailAvailability } from './checkEmailAvailability'; +import { setEmail } from './setEmail'; +import { setUserAvatar } from './setUserAvatar'; import { setStatusText } from './setStatusText'; import { checkUsernameAvailability } from './checkUsernameAvailability'; import { callbacks } from '../../../../lib/callbacks'; diff --git a/apps/meteor/app/lib/server/functions/setEmail.ts b/apps/meteor/app/lib/server/functions/setEmail.ts index a23ab5ff8f8b..47bf47203305 100644 --- a/apps/meteor/app/lib/server/functions/setEmail.ts +++ b/apps/meteor/app/lib/server/functions/setEmail.ts @@ -6,7 +6,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP import { RateLimiter, validateEmailDomain } from '../lib'; import * as Mailer from '../../../mailer/server/api'; import { settings } from '../../../settings/server'; -import { checkEmailAvailability } from '.'; +import { checkEmailAvailability } from './checkEmailAvailability'; let html = ''; Meteor.startup(() => { diff --git a/apps/meteor/app/lib/server/functions/setUsername.ts b/apps/meteor/app/lib/server/functions/setUsername.ts index 396f3a064cc2..064c06f03188 100644 --- a/apps/meteor/app/lib/server/functions/setUsername.ts +++ b/apps/meteor/app/lib/server/functions/setUsername.ts @@ -9,7 +9,8 @@ import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { RateLimiter } from '../lib'; import { addUserToRoom } from './addUserToRoom'; -import { saveUserIdentity, setUserAvatar } from '.'; +import { saveUserIdentity } from './saveUserIdentity'; +import { setUserAvatar } from './setUserAvatar'; import { checkUsernameAvailability } from './checkUsernameAvailability'; import { getAvatarSuggestionForUser } from './getAvatarSuggestionForUser'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/lib/server/index.ts b/apps/meteor/app/lib/server/index.ts index 17ef46312d2d..597d03752dcc 100644 --- a/apps/meteor/app/lib/server/index.ts +++ b/apps/meteor/app/lib/server/index.ts @@ -53,4 +53,3 @@ import './methods/updateMessage'; import './methods/saveCustomFields'; export * from './lib'; -export * from './functions'; diff --git a/apps/meteor/app/lib/server/lib/bugsnag.ts b/apps/meteor/app/lib/server/lib/bugsnag.ts index a7b907064b83..b015ba44458a 100644 --- a/apps/meteor/app/lib/server/lib/bugsnag.ts +++ b/apps/meteor/app/lib/server/lib/bugsnag.ts @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import Bugsnag from '@bugsnag/js'; import { settings } from '../../../settings/server'; -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; import { Logger } from '../../../logger/server'; const logger = new Logger('bugsnag'); diff --git a/apps/meteor/app/lib/server/methods/addUsersToRoom.ts b/apps/meteor/app/lib/server/methods/addUsersToRoom.ts index 29bed563771c..3f342873250d 100644 --- a/apps/meteor/app/lib/server/methods/addUsersToRoom.ts +++ b/apps/meteor/app/lib/server/methods/addUsersToRoom.ts @@ -7,7 +7,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { addUserToRoom } from '../functions'; +import { addUserToRoom } from '../functions/addUserToRoom'; import { callbacks } from '../../../../lib/callbacks'; import { Federation } from '../../../../server/services/federation/Federation'; import { i18n } from '../../../../server/lib/i18n'; diff --git a/apps/meteor/app/lib/server/methods/createChannel.ts b/apps/meteor/app/lib/server/methods/createChannel.ts index 82ec272c50e1..d0137a76fad0 100644 --- a/apps/meteor/app/lib/server/methods/createChannel.ts +++ b/apps/meteor/app/lib/server/methods/createChannel.ts @@ -5,7 +5,7 @@ import type { ICreatedRoom } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { createRoom } from '../functions'; +import { createRoom } from '../functions/createRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts index 67bcb671e479..cf3f27eab109 100644 --- a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts +++ b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts @@ -5,7 +5,7 @@ import type { ICreatedRoom } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { createRoom } from '../functions'; +import { createRoom } from '../functions/createRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts index d13ec185a205..f11edf1cba12 100644 --- a/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts +++ b/apps/meteor/app/lib/server/methods/deleteUserOwnAccount.ts @@ -6,7 +6,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; -import { deleteUser } from '../functions'; +import { deleteUser } from '../functions/deleteUser'; import { AppEvents, Apps } from '../../../../ee/server/apps/orchestrator'; import { trim } from '../../../../lib/utils/stringUtils'; diff --git a/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts b/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts index de7102e69886..80b5428efbef 100644 --- a/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts +++ b/apps/meteor/app/lib/server/methods/executeSlashCommandPreview.ts @@ -2,7 +2,7 @@ import type { IMessage, RequiredField, SlashCommandPreviewItem } from '@rocket.c import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { slashCommands } from '../../../utils/server'; +import { slashCommands } from '../../../utils/server/slashCommand'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts b/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts index 5a0cf2f736a2..dcb39c466063 100644 --- a/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts +++ b/apps/meteor/app/lib/server/methods/getSlashCommandPreviews.ts @@ -2,7 +2,7 @@ import type { IMessage, RequiredField, SlashCommandPreviews } from '@rocket.chat import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { slashCommands } from '../../../utils/server'; +import { slashCommands } from '../../../utils/server/slashCommand'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts b/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts index 245fdcba9db6..5724449a5790 100644 --- a/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts +++ b/apps/meteor/app/lib/server/methods/getUsernameSuggestion.ts @@ -1,7 +1,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Meteor } from 'meteor/meteor'; -import { generateUsernameSuggestion } from '../functions'; +import { generateUsernameSuggestion } from '../functions/getUsernameSuggestion'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts index 8445c08219ee..ab581dbe12d1 100644 --- a/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts +++ b/apps/meteor/app/lib/server/methods/insertOrUpdateUser.ts @@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { saveUser } from '../functions'; +import { saveUser } from '../functions/saveUser'; import { twoFactorRequired } from '../../../2fa/server/twoFactorRequired'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts b/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts index fa8af706fa02..31a46089e1f1 100644 --- a/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts +++ b/apps/meteor/app/lib/server/methods/joinDefaultChannels.ts @@ -3,7 +3,7 @@ import { Match, check } from 'meteor/check'; import type { IUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { addUserToDefaultChannels } from '../functions'; +import { addUserToDefaultChannels } from '../functions/addUserToDefaultChannels'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/joinRoom.ts b/apps/meteor/app/lib/server/methods/joinRoom.ts index 1f1c58eceb37..f344cca8ea21 100644 --- a/apps/meteor/app/lib/server/methods/joinRoom.ts +++ b/apps/meteor/app/lib/server/methods/joinRoom.ts @@ -6,7 +6,7 @@ import { Rooms, Users } from '@rocket.chat/models'; import { canAccessRoomAsync } from '../../../authorization/server'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { addUserToRoom } from '../functions'; +import { addUserToRoom } from '../functions/addUserToRoom'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; diff --git a/apps/meteor/app/lib/server/methods/leaveRoom.ts b/apps/meteor/app/lib/server/methods/leaveRoom.ts index 09d9e1e72e80..21d3ec3ee24a 100644 --- a/apps/meteor/app/lib/server/methods/leaveRoom.ts +++ b/apps/meteor/app/lib/server/methods/leaveRoom.ts @@ -5,7 +5,7 @@ import { Roles, Subscriptions, Rooms } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { removeUserFromRoom } from '../functions'; +import { removeUserFromRoom } from '../functions/removeUserFromRoom'; import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; import { hasRoleAsync } from '../../../authorization/server/functions/hasRole'; diff --git a/apps/meteor/app/lib/server/methods/sendMessage.ts b/apps/meteor/app/lib/server/methods/sendMessage.ts index 9d764c05fb6c..c5be1a3c2b8d 100644 --- a/apps/meteor/app/lib/server/methods/sendMessage.ts +++ b/apps/meteor/app/lib/server/methods/sendMessage.ts @@ -10,7 +10,7 @@ import { canSendMessageAsync } from '../../../authorization/server/functions/can import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { metrics } from '../../../metrics/server'; import { settings } from '../../../settings/server'; -import { sendMessage } from '../functions'; +import { sendMessage } from '../functions/sendMessage'; import { RateLimiter } from '../lib'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { i18n } from '../../../../server/lib/i18n'; diff --git a/apps/meteor/app/lib/server/methods/setEmail.ts b/apps/meteor/app/lib/server/methods/setEmail.ts index 559544dcb683..5f2e79a2163d 100644 --- a/apps/meteor/app/lib/server/methods/setEmail.ts +++ b/apps/meteor/app/lib/server/methods/setEmail.ts @@ -3,7 +3,7 @@ import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { settings } from '../../../settings/server'; -import { setEmail } from '../functions'; +import { setEmail } from '../functions/setEmail'; import { RateLimiter } from '../lib'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/lib/server/methods/setRealName.ts b/apps/meteor/app/lib/server/methods/setRealName.ts index c96940310caf..87fe74fef5b5 100644 --- a/apps/meteor/app/lib/server/methods/setRealName.ts +++ b/apps/meteor/app/lib/server/methods/setRealName.ts @@ -3,7 +3,7 @@ import { check } from 'meteor/check'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { settings } from '../../../settings/server'; -import { setRealName } from '../functions'; +import { setRealName } from '../functions/setRealName'; import { RateLimiter } from '../lib'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/app/lib/server/methods/unarchiveRoom.ts b/apps/meteor/app/lib/server/methods/unarchiveRoom.ts index bf7643799e74..b4f012dee07c 100644 --- a/apps/meteor/app/lib/server/methods/unarchiveRoom.ts +++ b/apps/meteor/app/lib/server/methods/unarchiveRoom.ts @@ -5,7 +5,7 @@ import { isRegisterUser } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { unarchiveRoom } from '../functions'; +import { unarchiveRoom } from '../functions/unarchiveRoom'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/lib/server/methods/updateMessage.ts b/apps/meteor/app/lib/server/methods/updateMessage.ts index e86451fef429..8fbc8fe59d0f 100644 --- a/apps/meteor/app/lib/server/methods/updateMessage.ts +++ b/apps/meteor/app/lib/server/methods/updateMessage.ts @@ -8,7 +8,7 @@ import { Messages, Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; import { canSendMessageAsync } from '../../../authorization/server/functions/canSendMessage'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; -import { updateMessage } from '../functions'; +import { updateMessage } from '../functions/updateMessage'; const allowedEditedFields = ['tshow', 'alias', 'attachments', 'avatar', 'emoji', 'msg']; diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.ts b/apps/meteor/app/livechat/imports/server/rest/upload.ts index f6f04ef1877f..9abf6d0524ce 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.ts +++ b/apps/meteor/app/livechat/imports/server/rest/upload.ts @@ -2,11 +2,11 @@ import filesize from 'filesize'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import { settings } from '../../../../settings/server'; -import { fileUploadIsValidContentType } from '../../../../utils/server'; import { FileUpload } from '../../../../file-upload/server'; import { API } from '../../../../api/server'; import { getUploadFormData } from '../../../../api/server/lib/getUploadFormData'; import { sendFileLivechatMessage } from '../../../server/methods/sendFileLivechatMessage'; +import { fileUploadIsValidContentType } from '../../../../utils/server/restrictions'; API.v1.addRoute('livechat/upload/:rid', { async post() { diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index d43067747d62..82ef0d623353 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -24,7 +24,7 @@ import { normalizeTransferredByData } from '../../lib/Helper'; import { findVisitorInfo } from '../lib/visitors'; import { canAccessRoomAsync } from '../../../../authorization/server'; import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; -import { addUserToRoom } from '../../../../lib/server/functions'; +import { addUserToRoom } from '../../../../lib/server/functions/addUserToRoom'; import { callbacks } from '../../../../../lib/callbacks'; import type { CloseRoomParams } from '../../lib/LivechatTyped'; import { isWidget } from '../../../../api/server/helpers/isWidget'; diff --git a/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts b/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts index c64f9e29c474..dd9a90b5e91a 100644 --- a/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts +++ b/apps/meteor/app/livechat/server/hooks/offlineMessageToChannel.ts @@ -2,7 +2,7 @@ import { LivechatDepartment, Users, Rooms } from '@rocket.chat/models'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; import { callbacks } from '../../../../lib/callbacks'; -import { sendMessage } from '../../../lib/server'; +import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { i18n } from '../../../../server/lib/i18n'; import { settings } from '../../../settings/server'; diff --git a/apps/meteor/app/livechat/server/lib/Analytics.js b/apps/meteor/app/livechat/server/lib/Analytics.js index e042a5c6a4b3..969376fa44d3 100644 --- a/apps/meteor/app/livechat/server/lib/Analytics.js +++ b/apps/meteor/app/livechat/server/lib/Analytics.js @@ -1,11 +1,11 @@ import moment from 'moment-timezone'; import { LivechatRooms } from '@rocket.chat/models'; -import { secondsToHHMMSS } from '../../../utils/server'; import { getTimezone } from '../../../utils/server/lib/getTimezone'; import { Logger } from '../../../logger/server'; import { i18n } from '../../../../server/lib/i18n'; import { callbacks } from '../../../../lib/callbacks'; +import { secondsToHHMMSS } from '../../../../lib/utils/secondsToHHMMSS'; const HOURS_IN_DAY = 24; const logger = new Logger('OmnichannelAnalytics'); diff --git a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts index a0f456fbe93c..450ee8593a2d 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts +++ b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts @@ -4,7 +4,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { settings } from '../../../../settings/server'; import { Livechat } from '../Livechat'; -import { secondsToHHMMSS } from '../../../../utils/server'; +import { secondsToHHMMSS } from '../../../../../lib/utils/secondsToHHMMSS'; import { findPercentageOfAbandonedRoomsAsync, findAllAverageOfChatDurationTimeAsync, diff --git a/apps/meteor/app/mail-messages/server/functions/sendMail.ts b/apps/meteor/app/mail-messages/server/functions/sendMail.ts index 881b5eb7f706..65f4d9d38578 100644 --- a/apps/meteor/app/mail-messages/server/functions/sendMail.ts +++ b/apps/meteor/app/mail-messages/server/functions/sendMail.ts @@ -5,7 +5,7 @@ import type { Filter } from 'mongodb'; import type { IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; -import { placeholders } from '../../../utils/server'; +import { placeholders } from '../../../utils/server/placeholders'; import { SystemLogger } from '../../../../server/lib/logger/system'; import * as Mailer from '../../../mailer/server/api'; import { generatePath } from '../../../../lib/utils/generatePath'; diff --git a/apps/meteor/app/message-pin/server/pinMessage.ts b/apps/meteor/app/message-pin/server/pinMessage.ts index 2b7419741474..18d7094223b4 100644 --- a/apps/meteor/app/message-pin/server/pinMessage.ts +++ b/apps/meteor/app/message-pin/server/pinMessage.ts @@ -8,7 +8,7 @@ import { Messages, Rooms, Subscriptions, Users } from '@rocket.chat/models'; import { settings } from '../../settings/server'; import { callbacks } from '../../../lib/callbacks'; -import { isTheLastMessage } from '../../lib/server'; +import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; diff --git a/apps/meteor/app/message-star/server/starMessage.ts b/apps/meteor/app/message-star/server/starMessage.ts index 61317f9a5ab9..d5225de2fda9 100644 --- a/apps/meteor/app/message-star/server/starMessage.ts +++ b/apps/meteor/app/message-star/server/starMessage.ts @@ -4,7 +4,7 @@ import type { IMessage } from '@rocket.chat/core-typings'; import { Messages, Subscriptions, Rooms } from '@rocket.chat/models'; import { settings } from '../../settings/server'; -import { isTheLastMessage } from '../../lib/server'; +import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; import { canAccessRoomAsync, roomAccessAttributes } from '../../authorization/server'; import { Apps, AppEvents } from '../../../ee/server/apps/orchestrator'; diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts index 16db1f6a0a24..b827a4330d5b 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts @@ -8,7 +8,10 @@ import type { IUser, IIncomingMessage, IPersonalAccessToken } from '@rocket.chat import { CredentialTokens, Rooms, Users } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; -import { saveUserIdentity, createRoom, generateUsernameSuggestion, addUserToRoom } from '../../../lib/server/functions'; +import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; +import { createRoom } from '../../../lib/server/functions/createRoom'; +import { generateUsernameSuggestion } from '../../../lib/server/functions/getUsernameSuggestion'; +import { saveUserIdentity } from '../../../lib/server/functions/saveUserIdentity'; import { SAMLServiceProvider } from './ServiceProvider'; import type { IServiceProviderOptions } from '../definition/IServiceProviderOptions'; import type { ISAMLAction } from '../definition/ISAMLAction'; diff --git a/apps/meteor/app/metrics/server/lib/collectMetrics.ts b/apps/meteor/app/metrics/server/lib/collectMetrics.ts index 44e193fa2fe5..46dcfb588ec0 100644 --- a/apps/meteor/app/metrics/server/lib/collectMetrics.ts +++ b/apps/meteor/app/metrics/server/lib/collectMetrics.ts @@ -9,7 +9,7 @@ import { MongoInternals } from 'meteor/mongo'; import { Facts } from 'meteor/facts-base'; import { Statistics } from '@rocket.chat/models'; -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; import { getControl } from '../../../../server/lib/migrations'; import { settings } from '../../../settings/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; diff --git a/apps/meteor/app/oembed/server/server.ts b/apps/meteor/app/oembed/server/server.ts index 51702700ba10..8d4d00df81b1 100644 --- a/apps/meteor/app/oembed/server/server.ts +++ b/apps/meteor/app/oembed/server/server.ts @@ -16,7 +16,7 @@ import { Logger } from '../../logger/server'; import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; import { isURL } from '../../../lib/utils/isURL'; -import { Info } from '../../utils/server'; +import { Info } from '../../utils/rocketchat.info'; const log = new Logger('OEmbed'); // Detect encoding diff --git a/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts b/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts index 521e5c10d7d3..ace5790edd78 100644 --- a/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts +++ b/apps/meteor/app/push-notifications/server/methods/saveNotificationSettings.ts @@ -4,7 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import type { ISubscription } from '@rocket.chat/core-typings'; import { Subscriptions } from '@rocket.chat/models'; -import { getUserNotificationPreference } from '../../../utils/server'; +import { getUserNotificationPreference } from '../../../utils/server/getUserNotificationPreference'; const saveAudioNotificationValue = (subId: ISubscription['_id'], value: string) => value === 'default' ? Subscriptions.clearAudioNotificationValueById(subId) : Subscriptions.updateAudioNotificationValueById(subId, value); diff --git a/apps/meteor/app/reactions/server/setReaction.ts b/apps/meteor/app/reactions/server/setReaction.ts index 00f6bae48439..968eb32e41fe 100644 --- a/apps/meteor/app/reactions/server/setReaction.ts +++ b/apps/meteor/app/reactions/server/setReaction.ts @@ -7,7 +7,7 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { callbacks } from '../../../lib/callbacks'; import { emoji } from '../../emoji/server'; -import { isTheLastMessage } from '../../lib/server'; +import { isTheLastMessage } from '../../lib/server/functions/isTheLastMessage'; import { canAccessRoomAsync } from '../../authorization/server'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; import { AppEvents, Apps } from '../../../ee/server/apps/orchestrator'; diff --git a/apps/meteor/app/slackbridge/client/slackbridge_import.client.js b/apps/meteor/app/slackbridge/client/slackbridge_import.client.js index 656bda9a62b1..6aeffb7bef45 100644 --- a/apps/meteor/app/slackbridge/client/slackbridge_import.client.js +++ b/apps/meteor/app/slackbridge/client/slackbridge_import.client.js @@ -1,5 +1,5 @@ import { settings } from '../../settings/client'; -import { slashCommands } from '../../utils/client'; +import { slashCommands } from '../../utils/lib/slashCommand'; settings.onload('SlackBridge_Enabled', (key, value) => { if (value) { diff --git a/apps/meteor/app/slackbridge/server/RocketAdapter.js b/apps/meteor/app/slackbridge/server/RocketAdapter.js index 5582595c0533..4806b4a788c6 100644 --- a/apps/meteor/app/slackbridge/server/RocketAdapter.js +++ b/apps/meteor/app/slackbridge/server/RocketAdapter.js @@ -9,7 +9,9 @@ import { Messages, Rooms, Users } from '@rocket.chat/models'; import { rocketLogger } from './logger'; import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; -import { createRoom, sendMessage, setUserAvatar } from '../../lib/server'; +import { sendMessage } from '../../lib/server/functions/sendMessage'; +import { createRoom } from '../../lib/server/functions/createRoom'; +import { setUserAvatar } from '../../lib/server/functions/setUserAvatar'; import { sleep } from '../../../lib/utils/sleep'; export default class RocketAdapter { diff --git a/apps/meteor/app/slackbridge/server/SlackAdapter.js b/apps/meteor/app/slackbridge/server/SlackAdapter.js index e0f1458d8c83..abd397834579 100644 --- a/apps/meteor/app/slackbridge/server/SlackAdapter.js +++ b/apps/meteor/app/slackbridge/server/SlackAdapter.js @@ -11,7 +11,12 @@ import { slackLogger } from './logger'; import { SlackAPI } from './SlackAPI'; import { getUserAvatarURL } from '../../utils/server/getUserAvatarURL'; import { settings } from '../../settings/server'; -import { deleteMessage, updateMessage, addUserToRoom, removeUserFromRoom, unarchiveRoom, sendMessage } from '../../lib/server'; +import { sendMessage } from '../../lib/server/functions/sendMessage'; +import { deleteMessage } from '../../lib/server/functions/deleteMessage'; +import { addUserToRoom } from '../../lib/server/functions/addUserToRoom'; +import { removeUserFromRoom } from '../../lib/server/functions/removeUserFromRoom'; +import { unarchiveRoom } from '../../lib/server/functions/unarchiveRoom'; +import { updateMessage } from '../../lib/server/functions/updateMessage'; import { archiveRoom } from '../../lib/server/functions/archiveRoom'; import { saveRoomName, saveRoomTopic } from '../../channel-settings/server'; import { FileUpload } from '../../file-upload/server'; diff --git a/apps/meteor/app/slackbridge/server/slackbridge_import.server.js b/apps/meteor/app/slackbridge/server/slackbridge_import.server.js index 80b20076972f..131d40a79b2c 100644 --- a/apps/meteor/app/slackbridge/server/slackbridge_import.server.js +++ b/apps/meteor/app/slackbridge/server/slackbridge_import.server.js @@ -4,7 +4,7 @@ import { Rooms, Users } from '@rocket.chat/models'; import { SlackBridge } from './slackbridge'; import { msgStream } from '../../lib/server'; -import { slashCommands } from '../../utils/server'; +import { slashCommands } from '../../utils/server/slashCommand'; import { i18n } from '../../../server/lib/i18n'; async function SlackBridgeImport({ command, params, message, userId }) { diff --git a/apps/meteor/app/slashcommands-hide/server/hide.ts b/apps/meteor/app/slashcommands-hide/server/hide.ts index b92ba8f24764..13ea222ca60e 100644 --- a/apps/meteor/app/slashcommands-hide/server/hide.ts +++ b/apps/meteor/app/slashcommands-hide/server/hide.ts @@ -3,7 +3,7 @@ import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; import type { IRoom, SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { settings } from '../../settings/server'; -import { slashCommands } from '../../utils/server'; +import { slashCommands } from '../../utils/server/slashCommand'; import { i18n } from '../../../server/lib/i18n'; import { hideRoomMethod } from '../../../server/methods/hideRoom'; diff --git a/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts b/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts index 3a033cec485a..361af2611000 100644 --- a/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts +++ b/apps/meteor/app/slashcommands-unarchiveroom/server/server.ts @@ -8,7 +8,7 @@ import { slashCommands } from '../../utils/lib/slashCommand'; import { settings } from '../../settings/server'; import { roomCoordinator } from '../../../server/lib/rooms/roomCoordinator'; import { RoomMemberActions } from '../../../definition/IRoomTypeConfig'; -import { unarchiveRoom } from '../../lib/server'; +import { unarchiveRoom } from '../../lib/server/functions/unarchiveRoom'; import { i18n } from '../../../server/lib/i18n'; slashCommands.add({ diff --git a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js index e44af22aa166..6337b287506a 100644 --- a/apps/meteor/app/statistics/server/lib/getAppsStatistics.js +++ b/apps/meteor/app/statistics/server/lib/getAppsStatistics.js @@ -1,7 +1,7 @@ import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus'; import { Apps } from '../../../../ee/server/apps'; -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; export function getAppsStatistics() { return { diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 47c0b5b22557..bb63199007b1 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -28,7 +28,8 @@ import { Analytics, Team, VideoConf } from '@rocket.chat/core-services'; import { UserStatus } from '@rocket.chat/core-typings'; import { settings } from '../../../settings/server'; -import { Info, getMongoInfo } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; +import { getMongoInfo } from '../../../utils/server/functions/getMongoInfo'; import { getControl } from '../../../../server/lib/migrations'; import { getStatistics as federationGetStatistics } from '../../../federation/server/functions/dashboard'; import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred'; diff --git a/apps/meteor/app/ui-master/server/inject.ts b/apps/meteor/app/ui-master/server/inject.ts index 0347c5dab0fb..d4cc1f55f7c5 100644 --- a/apps/meteor/app/ui-master/server/inject.ts +++ b/apps/meteor/app/ui-master/server/inject.ts @@ -6,7 +6,7 @@ import { WebApp } from 'meteor/webapp'; import { ReactiveDict } from 'meteor/reactive-dict'; import { Inject } from 'meteor/meteorhacks:inject-initial'; -import { getURL } from '../../utils/server'; +import { getURL } from '../../utils/server/getURL'; type Injection = | string diff --git a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts index 7d65d5e8088a..dceac6887191 100644 --- a/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts +++ b/apps/meteor/app/ui-message/client/popup/hooks/useComposerBoxPopupQueries.ts @@ -3,7 +3,7 @@ import { useQueries } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; import type { ComposerPopupOption } from '../../../../../client/views/room/contexts/ComposerPopupContext'; -import { slashCommands } from '../../../../utils/client'; +import { slashCommands } from '../../../../utils/lib/slashCommand'; import { useEnablePopupPreview } from './useEnablePopupPreview'; export const useComposerBoxPopupQueries = <T extends { _id: string; sort?: number }>(filter: unknown, popup?: ComposerPopupOption<T>) => { diff --git a/apps/meteor/app/user-status/server/methods/getUserStatusText.ts b/apps/meteor/app/user-status/server/methods/getUserStatusText.ts index e8e5b5a34f29..f298e8eab94e 100644 --- a/apps/meteor/app/user-status/server/methods/getUserStatusText.ts +++ b/apps/meteor/app/user-status/server/methods/getUserStatusText.ts @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; -import { getStatusText } from '../../../lib/server'; +import { getStatusText } from '../../../lib/server/functions/getStatusText'; declare module '@rocket.chat/ui-contexts' { // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/apps/meteor/app/utils/server/index.ts b/apps/meteor/app/utils/server/index.ts deleted file mode 100644 index e144683b4626..000000000000 --- a/apps/meteor/app/utils/server/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { getDefaultSubscriptionPref } from '../lib/getDefaultSubscriptionPref'; -export { Info } from '../rocketchat.info'; -export { getUserPreference } from './lib/getUserPreference'; -export { fileUploadIsValidContentType } from './restrictions'; -export { isDocker } from './functions/isDocker'; -export { getMongoInfo } from './functions/getMongoInfo'; -export { slashCommands } from './slashCommand'; -export { getUserNotificationPreference } from './getUserNotificationPreference'; -export { getAvatarColor } from '../lib/getAvatarColor'; -export { getURL } from './getURL'; -export { getValidRoomName } from './lib/getValidRoomName'; -export { placeholders } from './placeholders'; -export { secondsToHHMMSS } from '../../../lib/utils/secondsToHHMMSS'; diff --git a/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts b/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts index 852bb74b6765..613b059940f2 100644 --- a/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts +++ b/apps/meteor/app/version-check/server/functions/checkVersionUpdate.ts @@ -4,7 +4,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import { getNewUpdates } from './getNewUpdates'; import { settings } from '../../../settings/server'; -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; import logger from '../logger'; import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmins'; import { i18n } from '../../../../server/lib/i18n'; diff --git a/apps/meteor/app/version-check/server/functions/getNewUpdates.ts b/apps/meteor/app/version-check/server/functions/getNewUpdates.ts index 10b823f75ed3..ae04646b8966 100644 --- a/apps/meteor/app/version-check/server/functions/getNewUpdates.ts +++ b/apps/meteor/app/version-check/server/functions/getNewUpdates.ts @@ -4,7 +4,7 @@ import { Settings } from '@rocket.chat/models'; import { check, Match } from 'meteor/check'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; -import { Info } from '../../../utils/server'; +import { Info } from '../../../utils/rocketchat.info'; import { getWorkspaceAccessToken } from '../../../cloud/server'; export const getNewUpdates = async () => { diff --git a/apps/meteor/client/providers/ServerProvider.tsx b/apps/meteor/client/providers/ServerProvider.tsx index 321af5770918..8ff767b20933 100644 --- a/apps/meteor/client/providers/ServerProvider.tsx +++ b/apps/meteor/client/providers/ServerProvider.tsx @@ -16,8 +16,8 @@ import { compile } from 'path-to-regexp'; import type { FC } from 'react'; import React from 'react'; -import { Info as info } from '../../app/utils/client'; import { sdk } from '../../app/utils/client/lib/SDKClient'; +import { Info as info } from '../../app/utils/rocketchat.info'; const absoluteUrl = (path: string): string => Meteor.absoluteUrl(path); diff --git a/apps/meteor/client/views/root/OutermostErrorBoundary.tsx b/apps/meteor/client/views/root/OutermostErrorBoundary.tsx index 436e69e2f0fd..90a83400420a 100644 --- a/apps/meteor/client/views/root/OutermostErrorBoundary.tsx +++ b/apps/meteor/client/views/root/OutermostErrorBoundary.tsx @@ -5,7 +5,7 @@ import type { ReactNode } from 'react'; import React from 'react'; import { ErrorBoundary } from 'react-error-boundary'; -import { Info } from '../../../app/utils/client'; +import { Info } from '../../../app/utils/rocketchat.info'; import AppErrorPage from './AppErrorPage'; declare global { diff --git a/apps/meteor/ee/client/apps/communication/websockets.js b/apps/meteor/ee/client/apps/communication/websockets.js index 932fd062168f..77ef2c92569e 100644 --- a/apps/meteor/ee/client/apps/communication/websockets.js +++ b/apps/meteor/ee/client/apps/communication/websockets.js @@ -1,8 +1,8 @@ import { Emitter } from '@rocket.chat/emitter'; import { CachedCollectionManager } from '../../../../app/ui-cached-collection/client'; -import { slashCommands } from '../../../../app/utils/client'; import { sdk } from '../../../../app/utils/client/lib/SDKClient'; +import { slashCommands } from '../../../../app/utils/lib/slashCommand'; export const AppEvents = Object.freeze({ APP_ADDED: 'app/added', diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index 790e5a87a19d..8b75a4a4b0de 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -19,7 +19,7 @@ import { sendMessagesToAdmins } from '../../../../server/lib/sendMessagesToAdmin import { getPaginationItems } from '../../../../app/api/server/helpers/getPaginationItems'; import type { APIClass } from '../../../../app/api/server'; import { API } from '../../../../app/api/server'; -import { Info } from '../../../../app/utils/server'; +import { Info } from '../../../../app/utils/rocketchat.info'; import type { AppServerOrchestrator } from '../orchestrator'; import { Apps } from '../orchestrator'; import { i18n } from '../../../../server/lib/i18n'; diff --git a/apps/meteor/ee/server/apps/marketplace/appInstall.ts b/apps/meteor/ee/server/apps/marketplace/appInstall.ts index 5f83b1d8e0c4..f58b0e3123aa 100644 --- a/apps/meteor/ee/server/apps/marketplace/appInstall.ts +++ b/apps/meteor/ee/server/apps/marketplace/appInstall.ts @@ -3,7 +3,7 @@ import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { getWorkspaceAccessToken } from '../../../../app/cloud/server'; import { settings } from '../../../../app/settings/server'; -import { Info } from '../../../../app/utils/server'; +import { Info } from '../../../../app/utils/rocketchat.info'; type installAction = 'install' | 'update' | 'uninstall'; diff --git a/apps/meteor/ee/server/lib/deviceManagement/session.ts b/apps/meteor/ee/server/lib/deviceManagement/session.ts index f7f67a1c93ca..6c74bd42e0d4 100644 --- a/apps/meteor/ee/server/lib/deviceManagement/session.ts +++ b/apps/meteor/ee/server/lib/deviceManagement/session.ts @@ -8,7 +8,7 @@ import * as Mailer from '../../../../app/mailer/server/api'; import { settings } from '../../../../app/settings/server'; import { UAParserDesktop, UAParserMobile } from '../../../../app/statistics/server/lib/UAParserCustom'; import { deviceManagementEvents } from '../../../../server/services/device-management/events'; -import { getUserPreference } from '../../../../app/utils/server'; +import { getUserPreference } from '../../../../app/utils/server/lib/getUserPreference'; import { t } from '../../../../app/utils/lib/i18n'; let mailTemplates: string; diff --git a/apps/meteor/ee/server/lib/ldap/Manager.ts b/apps/meteor/ee/server/lib/ldap/Manager.ts index 5a8a45c96a8f..a1f6ef27e722 100644 --- a/apps/meteor/ee/server/lib/ldap/Manager.ts +++ b/apps/meteor/ee/server/lib/ldap/Manager.ts @@ -9,11 +9,13 @@ import { LDAPDataConverter } from '../../../../server/lib/ldap/DataConverter'; import { LDAPConnection } from '../../../../server/lib/ldap/Connection'; import { LDAPManager } from '../../../../server/lib/ldap/Manager'; import { logger, searchLogger, mapLogger } from '../../../../server/lib/ldap/Logger'; -import { addUserToRoom, removeUserFromRoom, createRoom } from '../../../../app/lib/server/functions'; +import { addUserToRoom } from '../../../../app/lib/server/functions/addUserToRoom'; +import { createRoom } from '../../../../app/lib/server/functions/createRoom'; +import { removeUserFromRoom } from '../../../../app/lib/server/functions/removeUserFromRoom'; import { syncUserRoles } from '../syncUserRoles'; import { ensureArray } from '../../../../lib/utils/arrayUtils'; import { copyCustomFieldsLDAP } from './copyCustomFieldsLDAP'; -import { getValidRoomName } from '../../../../app/utils/server'; +import { getValidRoomName } from '../../../../app/utils/server/lib/getValidRoomName'; export class LDAPEEManager extends LDAPManager { public static async sync(): Promise<void> { diff --git a/apps/meteor/ee/server/lib/oauth/Manager.ts b/apps/meteor/ee/server/lib/oauth/Manager.ts index 19dbc743595f..9ce85ee5d964 100644 --- a/apps/meteor/ee/server/lib/oauth/Manager.ts +++ b/apps/meteor/ee/server/lib/oauth/Manager.ts @@ -1,10 +1,11 @@ import { Roles, Rooms } from '@rocket.chat/models'; import type { IUser } from '@rocket.chat/core-typings'; -import { addUserToRoom, createRoom } from '../../../../app/lib/server/functions'; +import { addUserToRoom } from '../../../../app/lib/server/functions/addUserToRoom'; +import { createRoom } from '../../../../app/lib/server/functions/createRoom'; import { Logger } from '../../../../app/logger/server'; import { syncUserRoles } from '../syncUserRoles'; -import { getValidRoomName } from '../../../../app/utils/server'; +import { getValidRoomName } from '../../../../app/utils/server/lib/getValidRoomName'; const logger = new Logger('OAuth'); diff --git a/apps/meteor/ee/server/local-services/federation/infrastructure/rocket-chat/slash-commands/index.ts b/apps/meteor/ee/server/local-services/federation/infrastructure/rocket-chat/slash-commands/index.ts index 78c89989de10..43fdba34cca8 100644 --- a/apps/meteor/ee/server/local-services/federation/infrastructure/rocket-chat/slash-commands/index.ts +++ b/apps/meteor/ee/server/local-services/federation/infrastructure/rocket-chat/slash-commands/index.ts @@ -2,7 +2,7 @@ import { FederationEE } from '@rocket.chat/core-services'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { executeSlashCommand } from './action'; -import { slashCommands } from '../../../../../../../app/utils/server'; +import { slashCommands } from '../../../../../../../app/utils/server/slashCommand'; const EE_FEDERATION_COMMANDS = { dm: async (currentUserId: string, _: string, invitees: string[]): Promise<void> => diff --git a/apps/meteor/server/features/EmailInbox/EmailInbox_Outgoing.ts b/apps/meteor/server/features/EmailInbox/EmailInbox_Outgoing.ts index 9a89e69c293f..cdae2382f7ba 100644 --- a/apps/meteor/server/features/EmailInbox/EmailInbox_Outgoing.ts +++ b/apps/meteor/server/features/EmailInbox/EmailInbox_Outgoing.ts @@ -6,7 +6,7 @@ import { Messages, Uploads, LivechatRooms, Rooms, Users } from '@rocket.chat/mod import { callbacks } from '../../../lib/callbacks'; import { FileUpload } from '../../../app/file-upload/server'; -import { slashCommands } from '../../../app/utils/server'; +import { slashCommands } from '../../../app/utils/server/slashCommand'; import type { Inbox } from './EmailInbox'; import { inboxes } from './EmailInbox'; import { sendMessage } from '../../../app/lib/server/functions/sendMessage'; diff --git a/apps/meteor/server/importPackages.ts b/apps/meteor/server/importPackages.ts index 04d51f031d3b..fd6a7fb094ab 100644 --- a/apps/meteor/server/importPackages.ts +++ b/apps/meteor/server/importPackages.ts @@ -81,7 +81,6 @@ import '../app/search/server'; import '../app/discussion/server'; import '../app/mail-messages/server'; import '../app/user-status/server'; -import '../app/utils/server'; import '../app/metrics/server'; import '../app/notifications/server'; import '../app/ui-utils/server'; diff --git a/apps/meteor/server/lib/ldap/Manager.ts b/apps/meteor/server/lib/ldap/Manager.ts index 01acd6cc3d1f..9c321751d39e 100644 --- a/apps/meteor/server/lib/ldap/Manager.ts +++ b/apps/meteor/server/lib/ldap/Manager.ts @@ -15,7 +15,7 @@ import { getLDAPConditionalSetting } from './getLDAPConditionalSetting'; import { logger, authLogger, connLogger } from './Logger'; import type { IConverterOptions } from '../../../app/importer/server/classes/ImportDataConverter'; import { callbacks } from '../../../lib/callbacks'; -import { setUserAvatar } from '../../../app/lib/server/functions'; +import { setUserAvatar } from '../../../app/lib/server/functions/setUserAvatar'; import { omit } from '../../../lib/utils/omit'; export class LDAPManager { diff --git a/apps/meteor/server/lib/migrations.ts b/apps/meteor/server/lib/migrations.ts index cde20e9422d2..eef474b2df2f 100644 --- a/apps/meteor/server/lib/migrations.ts +++ b/apps/meteor/server/lib/migrations.ts @@ -1,7 +1,7 @@ import { Migrations } from '@rocket.chat/models'; import type { IControl } from '@rocket.chat/core-typings'; -import { Info } from '../../app/utils/server'; +import { Info } from '../../app/utils/rocketchat.info'; import { Logger } from './logger/Logger'; import { showErrorBox } from './logger/showBox'; import { sleep } from '../../lib/utils/sleep'; diff --git a/apps/meteor/server/methods/channelsList.ts b/apps/meteor/server/methods/channelsList.ts index f7a9fd2343d1..55d9c6260c90 100644 --- a/apps/meteor/server/methods/channelsList.ts +++ b/apps/meteor/server/methods/channelsList.ts @@ -7,7 +7,7 @@ import { Rooms, Subscriptions, Users } from '@rocket.chat/models'; import type { FindOptions } from 'mongodb'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; -import { getUserPreference } from '../../app/utils/server'; +import { getUserPreference } from '../../app/utils/server/lib/getUserPreference'; import { settings } from '../../app/settings/server'; import { trim } from '../../lib/utils/stringUtils'; diff --git a/apps/meteor/server/methods/deleteUser.ts b/apps/meteor/server/methods/deleteUser.ts index 1dc87f7f1dbd..518c95d34b5d 100644 --- a/apps/meteor/server/methods/deleteUser.ts +++ b/apps/meteor/server/methods/deleteUser.ts @@ -6,7 +6,7 @@ import { Users } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { callbacks } from '../../lib/callbacks'; -import { deleteUser } from '../../app/lib/server'; +import { deleteUser } from '../../app/lib/server/functions/deleteUser'; import { AppEvents, Apps } from '../../ee/server/apps/orchestrator'; declare module '@rocket.chat/ui-contexts' { diff --git a/apps/meteor/server/methods/saveUserProfile.ts b/apps/meteor/server/methods/saveUserProfile.ts index bf8be34b846f..318646623691 100644 --- a/apps/meteor/server/methods/saveUserProfile.ts +++ b/apps/meteor/server/methods/saveUserProfile.ts @@ -5,7 +5,8 @@ import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Users } from '@rocket.chat/models'; import type { UserStatus } from '@rocket.chat/core-typings'; -import { saveCustomFields, passwordPolicy } from '../../app/lib/server'; +import { saveCustomFields } from '../../app/lib/server/functions/saveCustomFields'; +import { passwordPolicy } from '../../app/lib/server'; import { validateUserEditing } from '../../app/lib/server/functions/saveUser'; import { settings as rcSettings } from '../../app/settings/server'; import { twoFactorRequired } from '../../app/2fa/server/twoFactorRequired'; diff --git a/apps/meteor/server/routes/avatar/utils.js b/apps/meteor/server/routes/avatar/utils.js index 10d6274bd4ad..5fd5f84999cf 100644 --- a/apps/meteor/server/routes/avatar/utils.js +++ b/apps/meteor/server/routes/avatar/utils.js @@ -4,7 +4,7 @@ import { Cookies } from 'meteor/ostrio:cookies'; import { hashLoginToken } from '@rocket.chat/account-utils'; import { Users } from '@rocket.chat/models'; -import { getAvatarColor } from '../../../app/utils/server'; +import { getAvatarColor } from '../../../app/utils/lib/getAvatarColor'; import { settings } from '../../../app/settings/server'; const FALLBACK_LAST_MODIFIED = 'Thu, 01 Jan 2015 00:00:00 GMT'; diff --git a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Message.ts b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Message.ts index e7d3e14fa4dc..476c7ce84ca7 100644 --- a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Message.ts +++ b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Message.ts @@ -5,8 +5,10 @@ import { Messages } from '@rocket.chat/models'; import { toInternalMessageFormat, toInternalQuoteMessageFormat } from '../converters/to-external-parser-formatter'; import type { FederatedUser } from '../../../domain/FederatedUser'; import type { FederatedRoom } from '../../../domain/FederatedRoom'; -import { deleteMessage, sendMessage, updateMessage } from '../../../../../../app/lib/server'; -import { getURL } from '../../../../../../app/utils/server'; +import { sendMessage } from '../../../../../../app/lib/server/functions/sendMessage'; +import { deleteMessage } from '../../../../../../app/lib/server/functions/deleteMessage'; +import { updateMessage } from '../../../../../../app/lib/server/functions/updateMessage'; +import { getURL } from '../../../../../../app/utils/server/getURL'; import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator'; import { executeSetReaction } from '../../../../../../app/reactions/server/setReaction'; diff --git a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts index 2facbedd2cd7..a341ce68ff9f 100644 --- a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts +++ b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts @@ -4,13 +4,15 @@ import { isDirectMessageRoom } from '@rocket.chat/core-typings'; import { Rooms, Subscriptions, MatrixBridgedRoom, Users } from '@rocket.chat/models'; import { saveRoomTopic } from '../../../../../../app/channel-settings/server'; -import { addUserToRoom, createRoom, removeUserFromRoom } from '../../../../../../app/lib/server'; +import { addUserToRoom } from '../../../../../../app/lib/server/functions/addUserToRoom'; +import { createRoom } from '../../../../../../app/lib/server/functions/createRoom'; +import { removeUserFromRoom } from '../../../../../../app/lib/server/functions/removeUserFromRoom'; import { settings } from '../../../../../../app/settings/server'; import { DirectMessageFederatedRoom, FederatedRoom } from '../../../domain/FederatedRoom'; import type { FederatedUser } from '../../../domain/FederatedUser'; import type { ROCKET_CHAT_FEDERATION_ROLES } from '../definitions/FederatedRoomInternalRoles'; import { getFederatedUserByInternalUsername } from './User'; -import { getValidRoomName } from '../../../../../../app/utils/server'; +import { getValidRoomName } from '../../../../../../app/utils/server/lib/getValidRoomName'; import { extractServerNameFromExternalIdentifier } from '../../matrix/converters/room/RoomReceiver'; type WithRequiredProperty<Type, Key extends keyof Type> = Type & { diff --git a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/User.ts b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/User.ts index 16d05fdb318c..b1dfd5da0e5d 100644 --- a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/User.ts +++ b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/User.ts @@ -2,7 +2,8 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Users, MatrixBridgedUser } from '@rocket.chat/models'; import { FederatedUser } from '../../../domain/FederatedUser'; -import { _setRealName as setRealName, setUserAvatar } from '../../../../../../app/lib/server'; +import { _setRealName as setRealName } from '../../../../../../app/lib/server/functions/setRealName'; +import { setUserAvatar } from '../../../../../../app/lib/server/functions/setUserAvatar'; import { extractServerNameFromExternalIdentifier } from '../../matrix/converters/room/RoomReceiver'; const createFederatedUserInstance = (externalUserId: string, user: IUser, remote = true): FederatedUser => { diff --git a/apps/meteor/server/services/federation/infrastructure/rocket-chat/slash-commands/index.ts b/apps/meteor/server/services/federation/infrastructure/rocket-chat/slash-commands/index.ts index 7b898382b1d8..102f3dce342d 100644 --- a/apps/meteor/server/services/federation/infrastructure/rocket-chat/slash-commands/index.ts +++ b/apps/meteor/server/services/federation/infrastructure/rocket-chat/slash-commands/index.ts @@ -2,7 +2,7 @@ import { Federation } from '@rocket.chat/core-services'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; import { executeSlashCommand } from './action'; -import { slashCommands } from '../../../../../../app/utils/server'; +import { slashCommands } from '../../../../../../app/utils/server/slashCommand'; const FEDERATION_COMMANDS: Record<string, (currentUserId: string, roomId: string, invitee: string) => Promise<void>> = { dm: async (currentUserId: string, roomId: string, invitee: string) => diff --git a/apps/meteor/server/startup/initialData.js b/apps/meteor/server/startup/initialData.js index b5c81314921a..9d135c507073 100644 --- a/apps/meteor/server/startup/initialData.js +++ b/apps/meteor/server/startup/initialData.js @@ -8,7 +8,7 @@ import { FileUpload } from '../../app/file-upload/server'; import { getUsersInRole } from '../../app/authorization/server'; import { addUserRolesAsync } from '../lib/roles/addUserRoles'; import { settings } from '../../app/settings/server'; -import { addUserToDefaultChannels } from '../../app/lib/server'; +import { addUserToDefaultChannels } from '../../app/lib/server/functions/addUserToDefaultChannels'; import { checkUsernameAvailability } from '../../app/lib/server/functions/checkUsernameAvailability'; import { validateEmail } from '../../lib/emailValidator'; diff --git a/apps/meteor/server/startup/serverRunning.js b/apps/meteor/server/startup/serverRunning.js index 03f063f843eb..ebc952acf712 100644 --- a/apps/meteor/server/startup/serverRunning.js +++ b/apps/meteor/server/startup/serverRunning.js @@ -6,7 +6,8 @@ import { Meteor } from 'meteor/meteor'; import { Users } from '@rocket.chat/models'; import { settings } from '../../app/settings/server'; -import { Info, getMongoInfo } from '../../app/utils/server'; +import { Info } from '../../app/utils/rocketchat.info'; +import { getMongoInfo } from '../../app/utils/server/functions/getMongoInfo'; import { sendMessagesToAdmins } from '../lib/sendMessagesToAdmins'; import { showErrorBox, showWarningBox, showSuccessBox } from '../lib/logger/showBox'; import { isRunningMs } from '../lib/isRunningMs'; From 8b6824dc7ccdd3b5dcb4a514b7f12ea472be9117 Mon Sep 17 00:00:00 2001 From: Tiago Evangelista Pinto <tiago.evangelista@rocket.chat> Date: Sat, 22 Jul 2023 16:49:19 -0300 Subject: [PATCH 148/149] chore: Use `screen.getBy*` instead of the render return (#29893) --- .../Omnichannel/modals/TranscriptModal.spec.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/meteor/tests/unit/client/components/Omnichannel/modals/TranscriptModal.spec.tsx b/apps/meteor/tests/unit/client/components/Omnichannel/modals/TranscriptModal.spec.tsx index 87b91591b738..1f7cc8c7964d 100644 --- a/apps/meteor/tests/unit/client/components/Omnichannel/modals/TranscriptModal.spec.tsx +++ b/apps/meteor/tests/unit/client/components/Omnichannel/modals/TranscriptModal.spec.tsx @@ -1,6 +1,6 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render, fireEvent, screen } from '@testing-library/react'; import { expect, spy } from 'chai'; import TranscriptModal from '../../../../../../client/components/Omnichannel/modals/TranscriptModal'; @@ -26,8 +26,8 @@ const defaultProps = { describe('components/Omnichannel/TranscriptModal', () => { it('should show Undo request button when roomOpen is true and transcriptRequest exist', () => { const onDiscardMock = spy(); - const { getByText } = render(<TranscriptModal {...defaultProps} onDiscard={onDiscardMock} />); - const undoRequestButton = getByText('Undo_request'); + render(<TranscriptModal {...defaultProps} onDiscard={onDiscardMock} />); + const undoRequestButton = screen.getByText('Undo_request'); fireEvent.click(undoRequestButton); @@ -35,16 +35,16 @@ describe('components/Omnichannel/TranscriptModal', () => { }); it('should show Request button when roomOpen is true and transcriptRequest not exist', () => { - const { getByRole } = render(<TranscriptModal {...{ ...defaultProps, room: { ...room, transcriptRequest: undefined } }} />); + render(<TranscriptModal {...{ ...defaultProps, room: { ...room, transcriptRequest: undefined } }} />); - const requestBtn = getByRole('button', { name: 'request-button' }); + const requestBtn = screen.getByRole('button', { name: 'request-button' }); expect(requestBtn).to.be.visible; }); it('should show Send button when roomOpen is false', () => { - const { getByRole } = render(<TranscriptModal {...{ ...defaultProps, room: { ...room, open: false } }} />); + render(<TranscriptModal {...{ ...defaultProps, room: { ...room, open: false } }} />); - const sendBtn = getByRole('button', { name: 'send-button' }); + const sendBtn = screen.getByRole('button', { name: 'send-button' }); expect(sendBtn).to.be.visible; }); }); From b011c17b0010b3d800469da6abd0b1e41aab0d89 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo <guilhermegazzo@gmail.com> Date: Sat, 22 Jul 2023 21:15:03 -0300 Subject: [PATCH 149/149] ci: update eslintcache key --- .github/workflows/ci-code-check.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-code-check.yml b/.github/workflows/ci-code-check.yml index 0f9b0a112f0c..5a556a1a8e29 100644 --- a/.github/workflows/ci-code-check.yml +++ b/.github/workflows/ci-code-check.yml @@ -53,8 +53,10 @@ jobs: if: matrix.check == 'lint' with: path: ./apps/meteor/.eslintcache - key: eslintcache-cache-${{ runner.OS }} + key: eslintcache-cache-${{ runner.OS }}-${{ hashFiles('yarn.lock') }}-${{ github.event.issue.number }} restore-keys: | + eslintcache-cache-${{ runner.OS }}-${{ hashFiles('yarn.lock') }} + eslintcache-cache-${{ runner.OS }} eslintcache-cache - name: Lint